Search code examples
iosswiftuinavigationcontrollernslayoutconstraintmodalviewcontroller

Dynamic height for UIViewController shown as modal popup


I am currently trying to implement a custom UIViewController which has a UINavigationController inside. This is a modal view that will be visible on the bottom of the screen and the user has different options to navigate inside this modal view. The background of the UIViewController is set to clear, so it shows as an overlay of the already visible screen.

I have now managed to set up and present my UIViewController like this:

    let modalController = ModalController()
    modalController.modalPresentationStyle = .overCurrentContext
    self.present(modalController, animated: true, completion: nil)

Inside this UIViewController I do this:

var popupController: UINavigationController = {
    let popupController = UINavigationController(rootViewController: ModalStart())
    return popupController
}()

var modalPopup: UIView = {
    let modalPopup = UIView()
    modalPopup.translatesAutoresizingMaskIntoConstraints = false
    modalPopup.backgroundColor = .white
    modalPopup.isUserInteractionEnabled = true
    return modalPopup
}()

view.addSubview(modalPopup)
modalPopup.addSubview(popupController.view)

modalPopup.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
modalPopup.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
modalPopup.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
modalPopup.heightAnchor.constraint(equalToConstant: 500).isActive = true

This works fine but as you might guess already, the height is set to a fixed constant of 500. This works on my iPhone X, but would be different on iPhone 5s, for example. It also doesn't allow me to change the height accordingly when I navigate through the UINavigationController, as the height of the different viewControllers will be different.

Does anybody know how I can achieve this? I have already added a custom ContainerView inside my rootViewController (ModalStart), but I can't directly access popupController.containerView.frame.height (as popupController is a UINavigationController). I also tried to make the heightAnchor a variable and set it in viewDidLayoutSubviews() of ModalStart(), but this doesn't work either and fires constantly with different heights (if I print the height, it sometimes returns 480, sometimes 0, very strange behavior there).

I am wondering what the best approach for my specific case might be. Anything pointing my towards the right direction is very much appreciated!


Solution

  • I'm gonna show you an easy way of doing it. From what I have seen, the best way to do it is to use a dataSource model for the modalView and calculate the height from there. For example, if you have an image and one label, which heights are known and if not can be calculated, you can send that as a dataSource for the modalView. Now, if the next modalView will only have 6 labels, again it will be easy to calculate the height that you need to have to fit them all. By using a dataSource model, you can then pre calculate all the height before the transition happens to the next modalView. I put together an easy example for you here: https://github.com/galots/VariableModal

    If you click in the button that says Present, the new viewController will be presented and the modalView will be shown with an initial constraint. Then if you click Next, the modal view height will be updated accordingly to the dataSource. In this case the dataSource is just an array of CGFloat, just to make it easy to understand. Also, see that the modalView is a subclass of UIView so you can update its height constraint as needed.

    Hope it helps.