I have a UIViewController
which have only a UIView
which covers 1/3 of the viewController from bottom. Like this
I want to present this viewController on an other ViewController. It should appear from bottom animated and it should dismiss to the bottom animated.
But I do not want it to cover the whole Screen. The viewController on which it is presented should be visible in the back.
It seems like a basic question But I am unable to get it done. Can someone please point me to the direction ?
Edit:
This is what I have tried so Far. I have created these classes
// MARK: - class MyFadeInFadeOutTransitioning: NSObject, UIViewControllerTransitioningDelegate { var backgroundColorAlpha: CGFloat = 0.5 var shoulDismiss = false func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? { let fadeInPresentAnimationController = MyFadeInPresentAnimationController() fadeInPresentAnimationController.backgroundColorAlpha = backgroundColorAlpha return fadeInPresentAnimationController } func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { let fadeOutDismissAnimationController = MyFadeOutDismissAnimationController() return fadeOutDismissAnimationController } } // MARK: - class MYFadeInPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning { let kPresentationDuration = 0.5 var backgroundColorAlpha: CGFloat? func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return kPresentationDuration } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)! toViewController.view.backgroundColor = UIColor.clear let toViewFrame = transitionContext.finalFrame(for: toViewController) let containerView = transitionContext.containerView if let pickerContainerView = toViewController.view.viewWithTag(kContainerViewTag) { let transform = CGAffineTransform(translationX: 0.0, y: pickerContainerView.frame.size.height) pickerContainerView.transform = transform } toViewController.view.frame = toViewFrame containerView.addSubview(toViewController.view) UIView.animate(withDuration: 0.3, delay: 0.0, options: .curveLinear , animations: { toViewController.view.backgroundColor = UIColor(white: 0.0, alpha: self.backgroundColorAlpha!) if let pickerContainerView = toViewController.view.viewWithTag(kContainerViewTag) { pickerContainerView.transform = CGAffineTransform.identity } }) { (finished) in transitionContext.completeTransition(true) } } } // MARK: - class MYFadeOutDismissAnimationController: NSObject, UIViewControllerAnimatedTransitioning { let kDismissalDuration = 0.15 func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return kDismissalDuration } func animateTransition(using transitionContext: UIViewControllerContextTransitioning) { let fromViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from)! let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)! let containerView = transitionContext.containerView containerView.addSubview(toViewController.view) containerView.sendSubview(toBack: toViewController.view) UIView.animate(withDuration: kDismissalDuration, delay: 0.0, options: .curveLinear, animations: { // fromViewController.view.backgroundColor = UIColor.clearColor() // if let pickerContainerView = toViewController.view.viewWithTag(kContainerViewTag) { // let transform = CGAffineTransformMakeTranslation(0.0, pickerContainerView.frame.size.height) // pickerContainerView.transform = transform // } fromViewController.view.alpha = 0.0 }) { (finished) in let canceled: Bool = transitionContext.transitionWasCancelled transitionContext.completeTransition(true) if !canceled { UIApplication.shared.keyWindow?.addSubview(toViewController.view) } } } }
And in the viewController which is being presented, I am doing as follows
var customTransitioningDelegate: MYFadeInFadeOutTransitioning? = MYFadeInFadeOutTransitioning() init() { super.init(nibName: "SomeNibName", bundle: Bundle.main) transitioningDelegate = customTransitioningDelegate modalPresentationStyle = .custom customTransitioningDelegate?.backgroundColorAlpha = 0.0 }
It do present the viewController and I can see the background viewController as well. But I want it to be presented from bottom with animation. And dismiss to bottom with animation. How can I do that ?
Start a segue from any object that implements an action method, such as a control or gesture recognizer. You may also start segues from table rows and collection view cells. Right-click the control or object in your current view controller. Drag the cursor to the view controller you want to present.
If you want to present a view controller over half a screen I suggest using the UIPresentationController
class it will allow you to set the frame of the view controller when it is presented. A word of advice, this method will stop the user interaction of the presentingViewController
until you dismiss the presentedViewController
, so if you want to show the view controller over half the screen while retaining user interaction with the presentingViewController
you should use container views like the other answers suggested. This is an example of a UIPresentationController class that does what you want
import UIKit class ForgotPasswordPresentationController: UIPresentationController{ let blurEffectView: UIVisualEffectView! var tapGestureRecognizer: UITapGestureRecognizer = UITapGestureRecognizer() func dismiss(){ self.presentedViewController.dismiss(animated: true, completion: nil) } override init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?) { let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark) blurEffectView = UIVisualEffectView(effect: blurEffect) super.init(presentedViewController: presentedViewController, presenting: presentingViewController) tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(self.dismiss)) blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] self.blurEffectView.isUserInteractionEnabled = true self.blurEffectView.addGestureRecognizer(tapGestureRecognizer) } override var frameOfPresentedViewInContainerView: CGRect{ return CGRect(origin: CGPoint(x: 0, y: self.containerView!.frame.height/2), size: CGSize(width: self.containerView!.frame.width, height: self.containerView!.frame.height/2)) } override func dismissalTransitionWillBegin() { self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in self.blurEffectView.alpha = 0 }, completion: { (UIViewControllerTransitionCoordinatorContext) in self.blurEffectView.removeFromSuperview() }) } override func presentationTransitionWillBegin() { self.blurEffectView.alpha = 0 self.containerView?.addSubview(blurEffectView) self.presentedViewController.transitionCoordinator?.animate(alongsideTransition: { (UIViewControllerTransitionCoordinatorContext) in self.blurEffectView.alpha = 1 }, completion: { (UIViewControllerTransitionCoordinatorContext) in }) } override func containerViewWillLayoutSubviews() { super.containerViewWillLayoutSubviews() presentedView!.layer.masksToBounds = true presentedView!.layer.cornerRadius = 10 } override func containerViewDidLayoutSubviews() { super.containerViewDidLayoutSubviews() self.presentedView?.frame = frameOfPresentedViewInContainerView blurEffectView.frame = containerView!.bounds } }
This also adds a blur view and a tap to dismiss when you tap outside the presentedViewController
frame. You need to set the transitioningDelegate
of the presentedViewController
and implement the
presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController?
method in there. Don't forget to also set modalPresentationStyle = .custom
of the presentedViewController
I find the usage of the UIPresentationController to be a much cleaner approach. Good luck
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With