Do not use a custom presentation controller, because you would lose the visuals and physics provided by Apple.
Present normally, then use systemLayoutSizeFitting
in viewDidLayoutSubviews
to adjust the frame to the minimum required size. Assuming your custom controller is CustomController
, replace it with this subclass and you are done.
import UIKit
final class ResizedViewController: CustomController {
private let cornerRadius: CGFloat = 22
private var containerView: UIView? { view.superview }
private var presentedView: UIView? { view }
private lazy var tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(resign))
override func viewDidLoad() {
super.viewDidLoad()
tapRecognizer.cancelsTouchesInView = true
view.layer.cornerRadius = cornerRadius
view.layer.cornerCurve = .continuous
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
containerView?.addGestureRecognizer(tapRecognizer)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let recognizer = containerView?.gestureRecognizers?.first(where: { $0 == tapRecognizer }) {
containerView?.removeGestureRecognizer(recognizer)
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
view.frame = frameOfPresentedViewInContainerView
}
private var frameOfPresentedViewInContainerView: CGRect {
guard let containerView = containerView else {
return CGRect.zero
}
guard let presentedView = presentedView else {
return CGRect.zero
}
let safeBounds = containerView.bounds.inset(by: containerView.safeAreaInsets)
let newSize = CGSize(
width: safeBounds.width,
height: UIView.layoutFittingCompressedSize.height
)
let newHeight = view.safeAreaInsets.bottom +
presentedView.systemLayoutSizeFitting(
newSize,
withHorizontalFittingPriority: .required,
verticalFittingPriority: .defaultLow
).height
return CGRect(
x: 0,
y: containerView.frame.height - newHeight,
width: safeBounds.width,
height: newHeight
)
}
@objc
private func resign() {
dismiss(animated: true, completion: nil)
}
}
The tap recognizer on the superview is needed to dismiss the controller tapping outside. The cornerRadius
from Apple is lost after altering the frame so I set it again.