Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Present modal view controller in half size parent controller

I am trying to present modal view controller on other viewcontroller sized to half parent view controller. But it always present in full screen view.

I have created freeform sized View controller in my storyboard with fixed frame size. 320 X 250.

var storyboard = UIStoryboard(name: "Main", bundle: nil) var pvc = storyboard.instantiateViewControllerWithIdentifier("CustomTableViewController") as ProductsTableViewController self.presentViewController(pvc, animated: true, completion: nil) 

I have tried to set frame.superview and it doesn't help.

Picture example

Please advice.

like image 286
Anton Avatar asked Mar 23 '15 20:03

Anton


People also ask

How do you present a modal view controller programmatically?

Use presentViewController:animated:completion: instead.) The default modal presentation style is a card. This shows the previous view controller at the top and allows the user to swipe away the presented view controller. This is the same for both programmatically created and storyboard created controllers.

What is child view controller in Swift?

Swift version: 5.6. View controller containment allows you to embed one view controller inside another, which can simplify and organize your code. It takes four steps: Call addChild() on your parent view controller, passing in your child. Set the child's frame to whatever you need, if you're using frames.


2 Answers

You can use a UIPresentationController to achieve this.

For this you let the presenting ViewController implement the UIViewControllerTransitioningDelegate and return your PresentationController for the half sized presentation:

func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {     return HalfSizePresentationController(presentedViewController: presented, presenting: presentingViewController) } 

When presenting you set the presentation style to .Custom and set your transitioning delegate:

pvc.modalPresentationStyle = .custom pvc.transitioningDelegate = self 

The presentation controller only returns the frame for your presented view controller:

class HalfSizePresentationController: UIPresentationController {     override var frameOfPresentedViewInContainerView: CGRect {         guard let bounds = containerView?.bounds else { return .zero }         return CGRect(x: 0, y: bounds.height / 2, width: bounds.width, height: bounds.height / 2)     } } 

Here is the working code in its entirety:

class ViewController: UIViewController, UIViewControllerTransitioningDelegate {      @IBAction func tap(sender: AnyObject) {         let storyboard = UIStoryboard(name: "Main", bundle: nil)         let pvc = storyboard.instantiateViewController(withIdentifier: "CustomTableViewController") as! UITableViewController          pvc.modalPresentationStyle = .custom         pvc.transitioningDelegate = self         pvc.view.backgroundColor = .red          present(pvc, animated: true)     }          func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {         return HalfSizePresentationController(presentedViewController: presented, presenting: presentingViewController)     } }  class HalfSizePresentationController: UIPresentationController {     override var frameOfPresentedViewInContainerView: CGRect {         guard let bounds = containerView?.bounds else { return .zero }         return CGRect(x: 0, y: bounds.height / 2, width: bounds.width, height: bounds.height / 2)     } }  
like image 128
Jannis Avatar answered Sep 27 '22 19:09

Jannis


It would be a a clean architect if you push some delegate methods of UIViewControllerTransitioningDelegate in your ViewController that want to be presented as half modal.

Assuming we have ViewControllerA present ViewControllerB with half modal.

in ViewControllerA just present ViewControllerB with custom modalPresentationStyle

func gotoVCB(_ sender: UIButton) {     let vc = ViewControllerB()     vc.modalPresentationStyle = .custom     present(vc, animated: true, completion: nil) } 

And in ViewControllerB:

import UIKit  final class ViewControllerB: UIViewController {  lazy var backdropView: UIView = {     let bdView = UIView(frame: self.view.bounds)     bdView.backgroundColor = UIColor.black.withAlphaComponent(0.5)     return bdView }()  let menuView = UIView() let menuHeight = UIScreen.main.bounds.height / 2 var isPresenting = false  init() {     super.init(nibName: nil, bundle: nil)     modalPresentationStyle = .custom     transitioningDelegate = self }  required init?(coder aDecoder: NSCoder) {     fatalError("init(coder:) has not been implemented") }  override func viewDidLoad() {     super.viewDidLoad()          view.backgroundColor = .clear     view.addSubview(backdropView)     view.addSubview(menuView)          menuView.backgroundColor = .red     menuView.translatesAutoresizingMaskIntoConstraints = false     menuView.heightAnchor.constraint(equalToConstant: menuHeight).isActive = true     menuView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true     menuView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true     menuView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true          let tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewControllerB.handleTap(_:)))     backdropView.addGestureRecognizer(tapGesture) }  @objc func handleTap(_ sender: UITapGestureRecognizer) {     dismiss(animated: true, completion: nil) } }  extension ViewControllerB: UIViewControllerTransitioningDelegate, UIViewControllerAnimatedTransitioning { func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {     return self }  func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {     return self }  func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {     return 1 }  func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {     let containerView = transitionContext.containerView     let toViewController = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to)     guard let toVC = toViewController else { return }     isPresenting = !isPresenting          if isPresenting == true {         containerView.addSubview(toVC.view)                  menuView.frame.origin.y += menuHeight         backdropView.alpha = 0                  UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseOut], animations: {             self.menuView.frame.origin.y -= self.menuHeight             self.backdropView.alpha = 1         }, completion: { (finished) in             transitionContext.completeTransition(true)         })     } else {         UIView.animate(withDuration: 0.4, delay: 0, options: [.curveEaseOut], animations: {             self.menuView.frame.origin.y += self.menuHeight             self.backdropView.alpha = 0         }, completion: { (finished) in             transitionContext.completeTransition(true)         })     } } } 

The result:

enter image description here

All code is published at my Github

like image 36
Khuong Avatar answered Sep 27 '22 18:09

Khuong