Last active
June 5, 2018 18:57
-
-
Save vialyx/28fbc1ecc901b385a190288249584719 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
enum ModalScaleState { | |
case presentation | |
case interaction | |
} | |
final class InteractiveModalPresentationController: UIPresentationController { | |
private let presentedYOffset: CGFloat = 150 | |
private var direction: CGFloat = 0 | |
private var state: ModalScaleState = .interaction | |
private lazy var dimmingView: UIView! = { | |
guard let container = containerView else { return nil } | |
let view = UIView(frame: container.bounds) | |
view.backgroundColor = UIColor.black.withAlphaComponent(0.75) | |
view.addGestureRecognizer( | |
UITapGestureRecognizer(target: self, action: #selector(didTap(tap:))) | |
) | |
return view | |
}() | |
override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) { | |
super.init(presentedViewController: presentedViewController, presenting: presentingViewController) | |
presentedViewController.view.addGestureRecognizer( | |
UIPanGestureRecognizer(target: self, action: #selector(didPan(pan:))) | |
) | |
} | |
@objc func didPan(pan: UIPanGestureRecognizer) { | |
guard let view = pan.view, let superView = view.superview, | |
let presented = presentedView, let container = containerView else { return } | |
let location = pan.translation(in: superView) | |
switch pan.state { | |
case .began: | |
presented.frame.size.height = container.frame.height | |
case .changed: | |
let velocity = pan.velocity(in: superView) | |
switch state { | |
case .interaction: | |
presented.frame.origin.y = location.y + presentedYOffset | |
case .presentation: | |
presented.frame.origin.y = location.y | |
} | |
direction = velocity.y | |
case .ended: | |
let maxPresentedY = container.frame.height - presentedYOffset | |
switch presented.frame.origin.y { | |
case 0...maxPresentedY: | |
changeScale(to: .interaction) | |
default: | |
presentedViewController.dismiss(animated: true, completion: nil) | |
} | |
default: | |
break | |
} | |
} | |
@objc func didTap(tap: UITapGestureRecognizer) { | |
presentedViewController.dismiss(animated: true, completion: nil) | |
} | |
func changeScale(to state: ModalScaleState) { | |
guard let presented = presentedView else { return } | |
UIView.animate(withDuration: 0.8, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 0.5, options: .curveEaseInOut, animations: { [weak self] in | |
guard let `self` = self else { return } | |
presented.frame = self.frameOfPresentedViewInContainerView | |
}, completion: { (isFinished) in | |
self.state = state | |
}) | |
} | |
override var frameOfPresentedViewInContainerView: CGRect { | |
guard let container = containerView else { return .zero } | |
return CGRect(x: 0, y: self.presentedYOffset, width: container.bounds.width, height: container.bounds.height - self.presentedYOffset) | |
} | |
override func presentationTransitionWillBegin() { | |
guard let container = containerView, | |
let coordinator = presentingViewController.transitionCoordinator else { return } | |
dimmingView.alpha = 0 | |
container.addSubview(dimmingView) | |
dimmingView.addSubview(presentedViewController.view) | |
coordinator.animate(alongsideTransition: { [weak self] context in | |
guard let `self` = self else { return } | |
self.dimmingView.alpha = 1 | |
}, completion: nil) | |
} | |
override func dismissalTransitionWillBegin() { | |
guard let coordinator = presentingViewController.transitionCoordinator else { return } | |
coordinator.animate(alongsideTransition: { [weak self] (context) -> Void in | |
guard let `self` = self else { return } | |
self.dimmingView.alpha = 0 | |
}, completion: nil) | |
} | |
override func dismissalTransitionDidEnd(_ completed: Bool) { | |
if completed { | |
dimmingView.removeFromSuperview() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment