How to update the frameOfPresentedViewInContainerView when using custom modal presentation?

1.1k views Asked by At

I'm using a custom UIPresentationController to present a view modally. After presenting the view, the first textfield in the presented view becomes the first responder and the keyboard shows up. To ensure that the view is still visible, I move it up. However, when I do this the frameOfPresentedViewInContainerView is not matching the actual frame of the view anymore. Because of this, when I tap on the view it's being dismissed, because there's a tapGestureRecogziner on the backgroundView which is on top of the presentingView. How to notify the presentingController that the frame/position of the presentedView has changed?

In the UIPresentationController:

    override var frameOfPresentedViewInContainerView: CGRect {
        var frame =  CGRect.zero
        let safeAreaBottom = self.presentingViewController.view.safeAreaInsets.bottom
        guard let height = presentedView?.frame.height else { return frame }
        if let containerBounds = containerView?.bounds {
            frame = CGRect(x: 0,
                           y: containerBounds.height - height - safeAreaBottom,
                           width: containerBounds.width,
                           height: height + safeAreaBottom)
        }
        return frame
    }

    override func presentationTransitionWillBegin() {
        if let containerView = self.containerView, let coordinator = presentingViewController.transitionCoordinator {
            containerView.addSubview(self.dimmedBackgroundView)
            self.dimmedBackgroundView.backgroundColor = .black
            self.dimmedBackgroundView.frame = containerView.bounds
            self.dimmedBackgroundView.alpha = 0
            coordinator.animate(alongsideTransition: { _ in
                self.dimmedBackgroundView.alpha = 0.5
            }, completion: nil)
        }
    }

Presenting the view modally:

            let overlayVC = CreateEventViewController()

            overlayVC.transitioningDelegate = self.transitioningDelegate
            overlayVC.modalPresentationStyle = .custom
            self.present(overlayVC, animated: true, completion: nil)

Animation when keyboard appears (in the presented view):

    @objc func animateWithKeyboard(notification: NSNotification) {
        let userInfo = notification.userInfo!
        guard let keyboardHeight = (userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue.height,
            let duration = userInfo[UIResponder.keyboardAnimationDurationUserInfoKey] as? Double,
            let curve = userInfo[UIResponder.keyboardAnimationCurveUserInfoKey] as? UInt else {
                return
        }

        // bottomContraint is the constraint that pins content to the bottom of the superview.
        let moveUp = (notification.name == UIResponder.keyboardWillShowNotification)
        bottomConstraint.constant = moveUp ? (keyboardHeight) : originalBottomValue

        let options = UIView.AnimationOptions(rawValue: curve << 16)
        UIView.animate(withDuration: duration, delay: 0,
                       options: options,
                       animations: {
                        self.view.layoutIfNeeded()
        }, completion: nil)
    }
1

There are 1 answers

0
Lokesh SN On

From the Apple documentation:

UIKit calls this method multiple times during the course of a presentation, so your implementation should return the same frame rectangle each time. Do not use this method to make changes to your view hierarchy or perform other one-time tasks.

AFAIK, if you specify frame through this variable, it's advised not to change it throughout the course of presentation. If you plan to play around with the frames, don't specify this variable and handle all the changes manually in your animator