Skip to content

Instantly share code, notes, and snippets.

@alfian0
Last active August 11, 2024 09:48
Show Gist options
  • Save alfian0/2a9fde9387cccf8cb298ad6caf2b04b9 to your computer and use it in GitHub Desktop.
Save alfian0/2a9fde9387cccf8cb298ad6caf2b04b9 to your computer and use it in GitHub Desktop.
import UIKit
class PresentationController: UIPresentationController {
private var chrome: UIView = UIView()
var interactor: Interactor? = nil
override var frameOfPresentedViewInContainerView: CGRect {
guard let containerView = containerView,
let presentedView = presentedView else { return .zero }
let inset: CGFloat = 0
var safeAreaFrame: CGRect = .zero
// Make sure to account for the safe area insets
if #available(iOS 11.0, *) {
safeAreaFrame = containerView.bounds
.inset(by: containerView.safeAreaInsets)
} else {
safeAreaFrame = containerView.bounds
}
let targetWidth = safeAreaFrame.width - (2 * inset)
let fittingSize = CGSize(
width: targetWidth,
height: UIView.layoutFittingCompressedSize.height
)
let targetHeight = presentedView.systemLayoutSizeFitting(
fittingSize, withHorizontalFittingPriority: .required,
verticalFittingPriority: .defaultLow).height
var frame = safeAreaFrame
frame.origin.x += inset
frame.origin.y += UIScreen.main.bounds.height - targetHeight - inset
frame.size.width = targetWidth
frame.size.height = targetHeight
return frame
}
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) {
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
chrome.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
chrome.alpha = 0.0
chrome.addGestureRecognizer(
UITapGestureRecognizer(target: self, action: #selector(tapGesture(sender:)))
)
presentedViewController.view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(panGesture(_:))))
}
@objc
private func panGesture(_ sender: UIPanGestureRecognizer) {
let percentThreshold: CGFloat = 0.3
let translation = sender.translation(in: sender.view)
let verticalMovement = translation.y / sender.view!.bounds.height
let downwardMovement = fmaxf(Float(verticalMovement), 0.0)
let downwardMovementPercent = fminf(downwardMovement, 1.0)
let progress = CGFloat(downwardMovementPercent)
guard let interactor = interactor else { return }
switch sender.state {
case .began:
presentingViewController.dismiss(animated: true, completion: nil)
case .changed:
interactor.shouldFinish = progress > percentThreshold
interactor.update(progress)
case .cancelled:
interactor.hasStarted = false
interactor.cancel()
case .ended:
interactor.hasStarted = false
interactor.shouldFinish ? interactor.finish() : interactor.cancel()
default:
break
}
}
override func containerViewWillLayoutSubviews() {
chrome.frame = containerView!.bounds
presentedView?.center = .zero
presentedView?.roundCorners([.topLeft, .topRight], radius: 21)
presentedView?.frame = frameOfPresentedViewInContainerView
}
override func presentationTransitionWillBegin() {
containerView?.insertSubview(chrome, at: 0)
presentedViewController
.transitionCoordinator?
.animate(alongsideTransition: { (_) in
self.chrome.alpha = 1.0
}, completion: nil)
}
override func dismissalTransitionWillBegin() {
presentedViewController
.transitionCoordinator?
.animate(alongsideTransition: { (_) in
self.chrome.alpha = 0.0
}, completion: nil)
}
override func dismissalTransitionDidEnd(_ completed: Bool) {
if completed {
chrome.removeFromSuperview()
}
}
@objc private func tapGesture(sender: UIGestureRecognizer) {
presentedViewController.dismiss(animated: true, completion: nil)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment