Intercepting and redefining modal presentation from a third-party framework

Ilya Bilov
Dec 15, 2017 · 4 min read

The Problem

Some time ago, here at Badoo, we introduced a third-party library to cover our project needs. It was working well, except for one detail: it was presenting UI using iOS modal presentation (typical full screen view controller which is shown from the bottom of the screen), but according to our requirements we needed to embed it in our custom view: Encounters card view, so that it looks like this advertisement built into the card:

self.present(controller, animated: true, completion: completion)
self.addChildViewController(controller)
pageView.addSubview(controller.view)
controller.didMove(toParentViewController: self)
public class SomeObject {
public func presentUI(from viewController: UIViewController)
public func dismissUI()
}
viewController.present(self.controller, animated: true, completion: completion)

Ideas

Our first thought was to override

func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Swift.Void)? = nil)
public override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Swift.Void)? = nil) {
self.addChildViewController(viewControllerToPresent)
viewControllerToPresent.view.frame = self.containerView.frame
self.containerView.addSubview(viewControllerToPresent.view)
viewControllerToPresent.didMove(toParentViewController: self)
completion?()
}

The Solution

The most important part of finding the solution was to closely look at all available APIs and try to estimate whether any of them could help us accomplish the task. Once we identified a couple of candidates among the available presentation APIs we tried a couple of them on practice and eventually UIPresentationController turned out to be the exact match to what we were looking for.
Here is what we did:

func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Swift.Void)? = nil)
public override func present(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Swift.Void)? = nil) {
viewControllerToPresent.modalPresentationStyle = .custom
viewControllerToPresent.modalTransitionStyle = .crossDissolve
viewControllerToPresent.transitioningDelegate = self
super.present(viewControllerToPresent, animated: flag,
completion: completion)
}
     extension MyPresentingViewController: UIViewControllerTransitioningDelegate {
public func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let presentationController = CustomUIPresentationController(presentedViewController: presented, presenting: presenting)
presentationController.customContainer = self.containerView
return presentationController
}
}
class CustomUIPresentationController: UIPresentationController {
public var customContainer: UIView!
public override var frameOfPresentedViewInContainerView: CGRect {
return self.customContainer.bounds
}

public override func presentationTransitionWillBegin() {
self.customContainer.addSubview(self.containerView!)
self.containerView?.frame = self.customContainer.bounds
}
}
Both presentations use self.present(animated:completion).

Conclusion

Sometimes Apple provides all the necessary tools and mechanisms to achieve a certain goal. If you don’t know how to implement something — don’t try to hack the system straight away! It is always better to spend some time looking around because there might be a native solution available which will save you hours of pain struggling with a hack later on.

Demo

Bumble Tech

This is the Bumble tech team blog focused on technology and…