2
votes

I want all of the functionality of a pageSheet UIModalPresentationStyle segue but I only want the presented ViewController to show half the screen (see the example in the image below).

enter image description here

I am presenting it modally using the pageSheet modalPresentationStyle but it always presents it at 100% height.

I haven't been able to figure out how to limit or modify a ViewController's height. I tried the following in my SecondViewController but it didn't work:

import UIKit

class SecondViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        self.preferredContentSize = CGSize(width: self.view.frame.width, height: 400)
    }
} 

I'm initiating the segue with Storyboard Segues, and a button that presents it modally: enter image description here

3
How are you setting up and presenting the Modal ViewController?ZGski
@ZGski Good question, I just updated my question with the details. I currently have a button on the MainViewController that, when pressed, initiates the modal.mpc75

3 Answers

2
votes

I figured out a way to do it, which I find to be pretty simple:

import UIKit

class SecondViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let newView = UIView(frame: CGRect(x: 0, y: 500, width: self.view.frame.width, height: 400))
        newView.backgroundColor = .yellow
        newView.layer.cornerRadius = 20

        self.view = UIView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height))

        // self.view is now a transparent view, so now I add newView to it and can size it however, I like.

        self.view.addSubview(newView)


        // works without the tap gesture just fine (only dragging), but I also wanted to be able to tap anywhere and dismiss it, so I added the gesture below
        let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
        self.view.addGestureRecognizer(tap)
    }

    @objc func handleTap(_ sender: UITapGestureRecognizer? = nil) {
        dismiss(animated: true, completion: nil)
    }
}
1
votes

In order to achieve you will need to subclass UIPresentationController and implement the protocol UIViewControllerTransitioningDelegate in the presenting controller and set transitioningDelegate and modalPresentationStyle of presented view controller as self(presenting view controller) and .custom respectively. Implement an optional function of UIViewControllerTransitioningDelegate:

func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source _: UIViewController) -> UIPresentationController?

and return the custom presentationController which sets the height of presented controller as per your requirement.

Basic code that might help:

class CustomPresentationController: UIPresentationController {
  var presentedViewHeight: CGFloat
  init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, presentedViewHeight: CGFloat) {
        self.presentedViewHeight = presentedViewHeight
        super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
    }

  override var frameOfPresentedViewInContainerView: CGRect {
        var frame: CGRect = .zero
        frame.size = CGSize(width: containerView!.bounds.width, height: presentedViewHeight)
        frame.origin.y = containerView!.frame.height - presentedViewHeight
        return frame
    }
}

Implementation of optional function:

func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source _: UIViewController) -> UIPresentationController? {
        let presentationController = CustomPresentationController(presentedViewController: presented, presenting: presenting, presentedViewHeight: 100)
        return presentationController
    }

You can also play with other optional functions and adding some other functionalities to CustomPresentationController like adding blur background, adding tap functionality and swipe gesture.

0
votes

We can add our view in UIActivityController and remove UIActivityController's default view and if you add navigation controller so you will get navigation also, so you can do half your controller by this:

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }
    
    @IBAction func actionPresent(_ sender: UIBarButtonItem) {
        let vc1 = storyboard?.instantiateViewController(withIdentifier: "ViewControllerCopy")
        let vc = ActivityViewController(controller: vc1!)
        self.present(vc, animated: true, completion: nil)
    }
}


class ActivityViewController: UIActivityViewController {

    private let controller: UIViewController!

    required init(controller: UIViewController) {
        self.controller = controller
        super.init(activityItems: [], applicationActivities: nil)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let subViews = self.view.subviews
        for view in subViews {
            view.removeFromSuperview()
        }

        self.addChild(controller)
        self.view.addSubview(controller.view)
    }
}

for example you can check this repo: https://github.com/SomuYadav/HalfViewControllerTransition