AAPLCustomPresentationController.swift 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. //
  2. // AAPLCustomPresentationController.swift
  3. // ComPDFKit_Tools
  4. //
  5. // Copyright © 2014-2024 PDF Technologies, Inc. All Rights Reserved.
  6. //
  7. // THIS SOURCE CODE AND ANY ACCOMPANYING DOCUMENTATION ARE PROTECTED BY INTERNATIONAL COPYRIGHT LAW
  8. // AND MAY NOT BE RESOLD OR REDISTRIBUTED. USAGE IS BOUND TO THE ComPDFKit LICENSE AGREEMENT.
  9. // UNAUTHORIZED REPRODUCTION OR DISTRIBUTION IS SUBJECT TO CIVIL AND CRIMINAL PENALTIES.
  10. // This notice may not be removed from this file.
  11. //
  12. import UIKit
  13. @objc public protocol AAPLCustomPresentationControllerDelegate: AnyObject {
  14. @objc optional func AAPLCustomPresentationControllerTap(_ customPresentationController: AAPLCustomPresentationController)
  15. }
  16. public class AAPLCustomPresentationController: UIPresentationController,UIViewControllerTransitioningDelegate,UIViewControllerAnimatedTransitioning {
  17. public weak var tapDelegate: AAPLCustomPresentationControllerDelegate?
  18. var dimmingView:UIView?
  19. var presentationWrappingView:UIView?
  20. let CORNER_RADIUS: CGFloat = 16.0
  21. public init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController) {
  22. super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
  23. presentedViewController.modalPresentationStyle = .custom
  24. }
  25. public override var presentedView: UIView? {
  26. return presentationWrappingView
  27. }
  28. public override func presentationTransitionWillBegin() {
  29. guard let presentedViewControllerView = super.presentedView else { return }
  30. let presentationWrapperView = UIView(frame: frameOfPresentedViewInContainerView)
  31. presentationWrapperView.layer.shadowOpacity = 0.44
  32. presentationWrapperView.layer.shadowRadius = 13
  33. presentationWrapperView.layer.shadowOffset = CGSize(width: 0, height: -6)
  34. presentationWrappingView = presentationWrapperView
  35. let presentationRoundedCornerView = UIView(frame: presentationWrapperView.bounds.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: -CORNER_RADIUS, right: 0)))
  36. presentationRoundedCornerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  37. presentationRoundedCornerView.layer.cornerRadius = CORNER_RADIUS
  38. presentationRoundedCornerView.layer.masksToBounds = true
  39. let presentedViewControllerWrapperView = UIView(frame: presentationRoundedCornerView.bounds.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: CORNER_RADIUS, right: 0)))
  40. presentedViewControllerWrapperView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  41. presentedViewControllerView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  42. presentedViewControllerView.frame = presentedViewControllerWrapperView.bounds
  43. presentedViewControllerWrapperView.addSubview(presentedViewControllerView)
  44. presentationRoundedCornerView.addSubview(presentedViewControllerWrapperView)
  45. presentationWrapperView.addSubview(presentationRoundedCornerView)
  46. if let containerView = self.containerView {
  47. let dimmingView = UIView(frame: containerView.bounds)
  48. dimmingView.backgroundColor = UIColor.black
  49. dimmingView.isOpaque = false
  50. dimmingView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
  51. dimmingView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dimmingViewTapped)))
  52. self.dimmingView = dimmingView
  53. containerView.addSubview(dimmingView)
  54. if let transitionCoordinator = presentingViewController.transitionCoordinator {
  55. self.dimmingView?.alpha = 0.0
  56. transitionCoordinator.animate(alongsideTransition: { (context) in
  57. self.dimmingView?.alpha = 0.5
  58. }, completion: nil)
  59. }
  60. }
  61. }
  62. public override func presentationTransitionDidEnd(_ completed: Bool) {
  63. if completed == false {
  64. self.presentationWrappingView = nil
  65. self.dimmingView = nil
  66. }
  67. }
  68. public override func dismissalTransitionWillBegin() {
  69. guard let transitionCoordinator = presentingViewController.transitionCoordinator else { return }
  70. transitionCoordinator.animate(alongsideTransition: { (context) in
  71. self.dimmingView?.alpha = 0.0
  72. }, completion: nil)
  73. }
  74. public override func dismissalTransitionDidEnd(_ completed: Bool) {
  75. if completed == true {
  76. self.presentationWrappingView = nil
  77. self.dimmingView = nil
  78. }
  79. }
  80. // MARK: - Layout
  81. public override func preferredContentSizeDidChange(forChildContentContainer container: UIContentContainer) {
  82. super.preferredContentSizeDidChange(forChildContentContainer: container)
  83. if container === presentedViewController {
  84. containerView?.setNeedsLayout()
  85. }
  86. }
  87. public override func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize) -> CGSize {
  88. if container === presentedViewController {
  89. return presentedViewController.preferredContentSize
  90. } else {
  91. return super.size(forChildContentContainer: container, withParentContainerSize: parentSize)
  92. }
  93. }
  94. public override var frameOfPresentedViewInContainerView: CGRect {
  95. let containerViewBounds = containerView?.bounds ?? CGRect.zero
  96. let presentedViewContentSize = size(forChildContentContainer: presentedViewController, withParentContainerSize: containerViewBounds.size)
  97. var presentedViewControllerFrame = containerViewBounds
  98. presentedViewControllerFrame.size.height = presentedViewContentSize.height
  99. presentedViewControllerFrame.origin.y = containerViewBounds.maxY - presentedViewContentSize.height
  100. return presentedViewControllerFrame
  101. }
  102. public override func containerViewWillLayoutSubviews() {
  103. super.containerViewWillLayoutSubviews()
  104. dimmingView?.frame = containerView?.bounds ?? CGRect.zero
  105. let width = min(containerView?.frame.size.width ?? 0, containerView?.frame.size.height ?? 0)
  106. presentationWrappingView?.frame = CGRect(x: (frameOfPresentedViewInContainerView.size.width - width)/2, y: frameOfPresentedViewInContainerView.origin.y, width: width, height: frameOfPresentedViewInContainerView.size.height)
  107. }
  108. // MARK: - Tap Gesture Recognizer
  109. @objc func dimmingViewTapped() {
  110. self.presentingViewController.dismiss(animated: true, completion: nil)
  111. self.tapDelegate?.AAPLCustomPresentationControllerTap?(self)
  112. }
  113. // MARK: - UIViewControllerAnimatedTransitioning
  114. public func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
  115. return transitionContext?.isAnimated ?? false ? 0.35 : 0
  116. }
  117. public func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
  118. guard let fromViewController = transitionContext.viewController(forKey: .from),
  119. let toViewController = transitionContext.viewController(forKey: .to) else {
  120. return
  121. }
  122. let containerView = transitionContext.containerView
  123. let toView = transitionContext.view(forKey: .to)
  124. let fromView = transitionContext.view(forKey: .from)
  125. let isPresenting = (fromViewController == presentingViewController)
  126. var fromViewFinalFrame = transitionContext.finalFrame(for: fromViewController)
  127. var toViewInitialFrame = transitionContext.initialFrame(for: toViewController)
  128. let toViewFinalFrame = transitionContext.finalFrame(for: toViewController)
  129. if(toView != nil) {
  130. containerView.addSubview(toView!)
  131. }
  132. if isPresenting {
  133. toViewInitialFrame.origin = CGPoint(x: containerView.bounds.minX, y: containerView.bounds.maxY)
  134. toViewInitialFrame.size = toViewFinalFrame.size
  135. toView?.frame = toViewInitialFrame
  136. } else {
  137. fromViewFinalFrame = fromView!.frame.offsetBy(dx: 0, dy: fromView!.frame.height)
  138. }
  139. let transitionDuration = self.transitionDuration(using: transitionContext)
  140. UIView.animate(withDuration: transitionDuration, animations: {
  141. if isPresenting {
  142. toView?.frame = toViewFinalFrame
  143. } else {
  144. fromView?.frame = fromViewFinalFrame
  145. }
  146. }) { (finished) in
  147. let wasCancelled = transitionContext.transitionWasCancelled
  148. transitionContext.completeTransition(!wasCancelled)
  149. }
  150. }
  151. // MARK: - UIViewControllerTransitioningDelegate
  152. public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
  153. assert(self.presentedViewController == presented, "You didn’t initialize (self) with the correct presentedViewController. Expected (presented), got (self.presentedViewController)")
  154. return self
  155. }
  156. public func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
  157. return self
  158. }
  159. public func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
  160. return self
  161. }
  162. }