Custom UIViewController transition bug in iOS 9 - ios

I encountered a strange bug. I am just using iOS's custom transitioning method for UIViewControllers using UIViewControllerTransitioningDelegate together with an implementation of UIViewControllerAnimatedTransitioning. It all seems to work fine, until I do exactly the following:
open the app
present another view controller with my custom transition
rotate to landscape
dismiss the just presented view controller
That's all! What happens now is the following: I see a large black bar on the right side of the initial view controller (as if that controller's view wasn't rotated to landscape).
The funny thing is this only goes wrong in iOS 9, in iOS 8 everything seems to work just fine. Did anything change with custom transition API I don't know of? Or is this simply a really nasty iOS 9 bug? If anyone can tell me what I did wrong or if anyone can provide me with a workaround I would really appreciate that!
These classes reproduce the problem:
import UIKit
class ViewController: UIViewController, UIViewControllerTransitioningDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "tap")
view.addGestureRecognizer(tapGestureRecognizer)
}
func tap() {
let controller = ModalViewController()
controller.transitioningDelegate = self
presentViewController(controller, animated: true, completion: nil)
}
func animationControllerForPresentedController(presented: UIViewController,
presentingController presenting: UIViewController,
sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return Transitioning()
}
func animationControllerForDismissedController(dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return Transitioning()
}
}
The presented view controller:
import UIKit
class ModalViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.redColor()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: "tap")
view.addGestureRecognizer(tapGestureRecognizer)
}
func tap() {
dismissViewControllerAnimated(true, completion: nil)
}
}
And finally the UIViewControllerAnimatedTransitioning implementation:
import UIKit
class Transitioning: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(transitionContext: UIViewControllerContextTransitioning?) -> NSTimeInterval {
return 0.5
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)
let containerView = transitionContext.containerView()
if let fromView = fromView, toView = toView {
containerView?.addSubview(fromView)
containerView?.addSubview(toView)
toView.alpha = 0
UIView.animateWithDuration(0.5, animations: {
toView.alpha = 1
}, completion: {
finished in
transitionContext.completeTransition(true)
})
}
}
}

I generally use the following in animateTransition:
toView.frame = fromView.frame
FYI, you don't have to add fromView to the hierarchy, as it's already there.

Related

UINavigationController cuts off content when height is changed? (gif)

I have put a UINavigationController into an expandable "Drawer".
My goal is to let each viewController in the navigation stack to have its own "preferred" height.
Let's say VC1 needs to be tall. When navigating back to VC1 from VC2 I want it to animate its height to be tall. The animation logic seems to be working, even with the interaction of swipe.
But for some reason, the viewControllers in the navigationController are "cut off". Their constraints are correct, but they aren't updated(?). Or a portion of the content simply won't render, until I touch the view again. The invisible area on the bottom will even accept touches.
Take a look:
The expected result is that the contentViewControllers (first and second) always extend to the bottom of the screen. They are constraint to do this, so the issue is that they won't "render"(?) during the transition.
In the UINavigationController's delegates, I do the following:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
transitionCoordinator?.animate(alongsideTransition: { (context) in
drawer.heightConstraint.constant = targetHeight
drawer.superview?.layoutIfNeeded()
}, completion: { (context) in
print("done")
})
}
The height change is perfect. But the content in the navigation won't comply. The navigationController is constrained to leading, trailing, bottom, and a stored heightConstraint that changes its constant.
As soon as I touch/drag the navigationController/content it instantly "renders the unrendered", and everything is fine. Why is this happening?
When inspecting the view hierarchy, it looks like this:
The NavigationController is as tall as it needs to be, but the content is the same height as the entire Drawer was when the transition started, and it doesn't update until I touch it.
Why?
Edit: I've pushed the code to my GitHub if you want to take a look. Beware though, there are several other issues there as well (animation etc.), don't mind them. I only want to know why the navigation won't render "future" heights.
You can solve the above problem by having a custom animationController for your navigationController and setting the appropriate frame for the destinationView in animateTransition function of your custom UIViewControllerAnimatedTransitioning implementation. The result would be like the one in the following gif image.
Your custom UIViewControllerAnimatedTransitioning may look like the one below.
final class TransitionAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let presenting: Bool
init(presenting: Bool) {
self.presenting = presenting
}
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return TimeInterval(UINavigationController.hideShowBarDuration)
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
guard let fromView = transitionContext.view(forKey: .from) else { return }
guard let toView = transitionContext.view(forKey: .to) else { return }
let duration = transitionDuration(using: transitionContext)
let container = transitionContext.containerView
if presenting {
container.addSubview(toView)
} else {
container.insertSubview(toView, belowSubview: fromView)
}
let toViewFrame = toView.frame
toView.frame = CGRect(x: presenting ? toView.frame.width : -toView.frame.width, y: toView.frame.origin.y, width: toView.frame.width, height: toView.frame.height)
UIView.animate(withDuration: duration, animations: {
toView.frame = toViewFrame
fromView.frame = CGRect(x: self.presenting ? -fromView.frame.width : fromView.frame.width, y: fromView.frame.origin.y, width: fromView.frame.width, height: fromView.frame.height)
}) { (finished) in
container.addSubview(toView)
transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
}
}
}
And, in your navigation controller provide custom animationController as follows.
func navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationController.Operation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning? {
switch operation {
case .push:
return TransitionAnimator(presenting: true)
case .pop:
return TransitionAnimator(presenting: false)
default:
return nil
}
}
PS :- I've also given a pull request in your github repo.
You can update controllers height yourself:
first you need to keep a reference to controllers:
class ContainedNavigationController: UINavigationController, Contained, UINavigationControllerDelegate {
private var controllers = [UIViewController]()
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) { controllers = viewControllers }
/* Rest of the class */
Then you can update their heights accordingly. Don't forget to add new controller.
private func updateHeights(to height: CGFloat, willShow controller: UIViewController) {
controller.view.frame.size.height = height
_ = controllers.map { $0.view.frame.size.height = height }
}
You can use it in your code like this:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if let superHeight = view.superview?.superview?.bounds.size.height, let drawer = (view.superview as? Drawer), let targetHeight = (viewController as? Contained)?.currentNotch.height(availableHeight: superHeight){
transitionCoordinator?.animate(alongsideTransition: { (context) in
drawer.heightConstraint.constant = targetHeight
drawer.superview?.layoutIfNeeded()
self.updateHeights(to: targetHeight, willShow: viewController) // <- Here
}, completion: { (context) in
self.updateHeights(to: targetHeight, willShow: viewController) // <- Here
})
}
}
Result:
Maybe this is not the cleanest code can done, but I just want to solve the issue and giving you the idea
Update
That shadow you have seen so far when you drag from the edge is a view called UIParallaxDimmingView. I have added a fix for that size too. So no more visual issues:
private func updateHeights(to height: CGFloat, willShow controller: UIViewController) {
controller.view.frame.size.height = height
_ = controllers.map { $0.view.frame.size.height = height }
guard controllers.contains(controller) else { return }
_ = controller.view.superview?.superview?.subviews.map {
guard "_UIParallaxDimmingView" == String(describing: type(of: $0)) else { return }
$0.frame.size.height = height
}
}
I have added a pull request from my fork.
You can to set size like bellow:
refrence -> https://github.com/satishVekariya/Drawer/tree/boom-diggy-boom
class FirstViewController: UIViewController, Contained {
// your code
override func updateViewConstraints() {
super.updateViewConstraints()
if let parentView = parent?.view {
view.frame.size.height = parentView.frame.height
}
}
}
Your navigation vc:
class ContainedNavigationController:UINavigationController, Contained, UINavigationControllerDelegate{
///Your code
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
viewController.updateViewConstraints()
// Your code start
// end
}
func navigationController(_ navigationController: UINavigationController, didShow viewController: UIViewController, animated: Bool) {
viewController.updateViewConstraints()
}
}
You can set height constraint and call layoutIfNeeded method without transition block.
Below is the code snippet for the same:
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
if let superHeight = view.superview?.superview?.bounds.size.height, let drawer = (view.superview as? Drawer), let targetHeight = (viewController as? Contained)?.currentNotch.height(availableHeight: superHeight){
drawer.heightConstraint.constant = targetHeight
drawer.layoutIfNeeded()
}
}
#sti Please let me know if it helps, please do up vote for the same

Swift. Xcode 10.2.1. Error Thread 1: EXC_BAD_ACCESS (code=2,...) - Navigation between screens

I learn the book iOS Animations by Tutorials
But I don't use Storyboard. I have several ViewControllers created programmatically. I have added RootVC in AppDelegate.swift. This application is working without navigation to the RootVC (going to the beginning) and the screens looks like that:
My question is about how to create such a navigation between different screens (ViewControllers) in Swift 4 (Xcode 10.2.1). It looks like there is an issue with looping... when the last ViewController instantiates the first RootVC and so on...
At the end I would like to have different custom navigation transitions on one ViewController (with .present() and with .navigationController?.pushViewController()
import UIKit
class FadePresentAnimator: NSObject, UIViewControllerAnimatedTransitioning {
let duration = 1.0
var presenting = true
var originFrame = CGRect.zero
var dismissCompletion: (()->Void)?
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return duration
}
//Setting the transition’s context
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
//Adding a fade transition
let containerView = transitionContext.containerView
let toView = transitionContext.view(forKey: .to)!
containerView.addSubview(toView)
toView.alpha = 0.0
UIView.animate(withDuration: duration,
animations: {
toView.alpha = 1.0
},
completion: { _ in
transitionContext.completeTransition(true)
}
)
}
}
import UIKit
//UIViewControllerTransitioningDelegate for self.present(self.nextScreen, animated: true, completion: nil)
//UINavigationControllerDelegate for self.navigationController?.pushViewController(self.nextScreen, animated: true)
class Screen3: UIViewController, UIViewControllerTransitioningDelegate, UINavigationControllerDelegate {
let nextScreen = RootVC() //4th Screen //<----- EXEC ERRROR
let transition = FadePresentAnimator()
let btnSize:CGFloat = 56.0
let btn1 = ClickableButton()
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Screen 3"
view.backgroundColor = HexColor.Named.BabyBlue
self.navigationController?.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
setupLayout()
}
private func setupLayout() {
//btn1:
view.addSubview(btn1)
btn1.setDefaultTitle(title: "▶") // ⏹ "▶" "■"
btn1.apply(height: btnSize)
btn1.apply(width: btnSize)
btn1.applyDefaultStyle()
if #available(iOS 11.0, *) {
btn1.alignXCenter(to: view.safeAreaLayoutGuide.centerXAnchor)
} else {
// Fallback on earlier versions
}
if #available(iOS 11.0, *) {
btn1.alignYCenter(to: view.safeAreaLayoutGuide.centerYAnchor)
} else {
// Fallback on earlier versions
}
btn1.clickHandler {
self.nextScreen.transitioningDelegate = self
//self.present(self.nextScreen, animated: true, completion: nil)
self.navigationController?.pushViewController(self.nextScreen, animated: true)
}
}
//forward
func animationController(forPresented presented: UIViewController,
presenting: UIViewController, source: UIViewController) ->
UIViewControllerAnimatedTransitioning? {
return transition
}
//backward
func animationController(forDismissed dismissed: UIViewController) ->
UIViewControllerAnimatedTransitioning? {
return nil
}
}
//
// AppDelegate.swift
// Anime-Control-01
//
import UIKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
//No Storyboards!
window = UIWindow(frame:UIScreen.main.bounds)
window?.makeKeyAndVisible()
let rootVC = RootVC() //RootVC.swift
let rootController = UINavigationController(rootViewController: rootVC)
window?.rootViewController = rootController
return true
}
}
It looks like there is an issue with looping... when the last ViewController instantiates the first RootVC and so on...
Yeah, you're totally right. The thing is you're creating next ViewController right when current ViewController is initialized.
The simplest way to fix this is to make nextScreen initialized lazily "on demand" by replacing this line
let nextScreen = RootVC()
by this one lazy var nextScreen = RootVC()
Or to create nextScreen variable right before the transition:
btn1.clickHandler {
let nextScreen = RootVC()
nextScreen.transitioningDelegate = self
navigationController?.pushViewController(nextScreen, animated: true)
}

Presenting view controller with custom animation using only code (No Segue)

Here's what I am tring to do:
Implement a side nav that will slide in with custom animation from left to right
I am refering to this: https://www.thorntech.com/2016/03/ios-tutorial-make-interactive-slide-menu-swift/
But this and all other tutorials that implement this functionality use segues to present the view.
So their code goes here:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?){
if let destinationViewController = segue.destinationViewController as? MenuViewController {
destinationViewController.transitioningDelegate = self
}
}
But this is not an option for me!
My entire view controller is created by code. Right now it's a dummy view controller and I am just trying to get it to slide in from left to right.
import UIKit
class SideNavVC: UIViewController {
static let sideNav = SideNavVC()
override func viewDidLoad() {
super.viewDidLoad()
let dummyView = UIView(frame: CGRect(x: 10, y: 200, width: 100, height: 100))
dummyView.backgroundColor = accentColor
view.addSubview(dummyView)
let closeBtn = UIButton(frame: CGRect(x: 4, y: 30, width: 200, height: 20))
closeBtn.addTarget(self, action: #selector(closeViewTapped), for: .touchUpInside)
closeBtn.setTitle("Close", for: .normal)
view.addSubview(closeBtn)
view.backgroundColor = confirmGreen
}
#objc func closeViewTapped(_ sender: UIButton) {
dismiss(animated: true, completion: nil)
}
}
And I am stuck on this step of the tutorial: https://www.thorntech.com/2016/03/ios-tutorial-make-interactive-slide-menu-swift/#four
This is how I am trying to set the delegate to self:
func addSlideGesture() {
let edgeSlide = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(presentSideNav))
edgeSlide.edges = .left
view.addGestureRecognizer(edgeSlide)
}
#objc func presentSideNav() {
if presentedViewController != SideNavVC.sideNav {
SideNavVC.sideNav.transitioningDelegate = self
SideNavVC.sideNav.modalPresentationStyle = .custom
self.present(SideNavVC.sideNav, animated: true, completion: nil)
}
}
This is where I was implementing my delegate (MainVC is from where the user will slide right)
extension MainVC: UIViewControllerTransitioningDelegate {
func animationControllerForPresentedController(presented: UIViewController, presentingController presenting: UIViewController, sourceController source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
print("Source: \(source)")
print("Destination \(presenting)")
if source == self && presenting == SideNavVC.sideNav {
print("PRESENTING SIDE NAV")
return RevealSideNav()
}
return nil
}
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
//Added a break point here. It is hitting.
return nil
}
}
Since my delegate was not calling, I decided to add presentationController(forPresented,presenting,source) and put a break point in there which was hitting. So I suspect that I am missing some part of the code that needs to go in there.
When I googled it, I found this UIPresentationController tutorial https://www.raywenderlich.com/915-uipresentationcontroller-tutorial-getting-started
But as I went through it, the more and more it started confusing me.
I am sure there is some small part that I am missing. Because if it works when the delegte is set in prepare for segue, then it should also work when calling present(_,animated,completion)
Can someone point me in right direction? How do I get the custom animations to trigger and the delegate methods to call? Does anyone know a tutorial that teaches how to do this using just code and no storyboard?
I referred to answer by #Womble
"UIViewControllerTransitioningDelegate method not called in iOS 7"
Turns out the method that I was using was wrong and Xcode didn't warn of this. Kinda odd.
func animationController(forPresented presented: UIViewController,
presenting: UIViewController,
source: UIViewController)
-> UIViewControllerAnimatedTransitioning?
{
print("Source: \(source)")
print("Destination \(presenting)")
// ... return your cached controller object here.
return RevealSideNav()
}

Custom transition

I have a navigation controller, and inside of that navigation controller I have a home screen, from the home screen I click a button which goes to another screen.
But the standard show animation when using a navigation controller is that it slides from the side, but what I want to do is that the view controller slides up from bottom of the screen and creates a sort of bouncing animation when it reaches the top.
Anyone who wanted to use custom transition two things to remember UIViewControllerAnimatedTransitioning and UIViewControllerTransitioningDelegate protocols. Now conform UIViewControllerAnimatedTransitioning inside your customclass inheriting from NSObject
import UIKit
class CustomPushAnimation: NSObject, UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.2
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerVw = transitionContext.containerView
let fromViewController = transitionContext.viewController(forKey: .from)
let toViewController = transitionContext.viewController(forKey: .to)
guard let fromVc = fromViewController, let toVc = toViewController else { return }
let finalFrame = transitionContext.finalFrame(for: toVc)
//For different animation you can play around this line by changing frame
toVc.view.frame = finalFrame.offsetBy(dx: 0, dy: finalFrame.size.height/2)
containerVw.addSubview(toVc.view)
UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: {
toVc.view.frame = finalFrame
fromVc.view.alpha = 0.5
}, completion: {(finished) in
transitionContext.completeTransition(finished)
fromVc.view.alpha = 1.0
})
}
}
The above method will take care of the animation. After that create
the above object and use inside yourViewController class
import UIKit
class YourViewController: UIViewController {
lazy var customPushAnimation: CustomPushAnimation = {
return CustomPushAnimation()
}()
func openViewControler() {
}let vc =//Assuming your view controller which you want to open
let navigationController = UINavigationController(rootViewController: vc)
//Set transitioningDelegate to invoke protocol method
navigationController.transitioningDelegate = self
present(navigationController, animated: true, completion: nil)
}
Note: In order to see the animation. Never set animation flag to false
while presenting the ViewController. Otherwise your animation will
never work.
Lastly implement the UIViewControllerTransitioningDelegate protocol method inside YourViewcontroller class
extension YourViewController: UIViewControllerTransitioningDelegate {
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return customPushAnimation
}
Whenever you present the viewcontroller above protocol method will
called and your animation magic will appear.

Why is the top layout guide moving in my iMessage extension

I have an iMessage extension and I'm having some issues with the top layout guide. I have an MSMessagesAppViewController that handles changes between presentation styles. In my extension I have a button. When it is clicked I transition to expanded presentation style and then present a view controller modally. Here's the problem: my UI in the second VC is getting hidden behind the top navigation bar. I thought this was strange as I set my constraints to the top layout guide. So I dug through my code and started debugging the top layout guide. I noticed that after I transition to expanded presentation style, topLayoutGuide.length = 86. That's how it should be. But when I present the second view controller modally, the top layout guide is reset to 0. Why isn't it 86 as it should be? Here is my code:
In my main viewController:
#IBAction func addStickerButtonPressed(_ sender: AnyObject) {
shouldPerformCreateSegue = true
theSender = sender
requestPresentationStyle(.expanded)
}
override func didTransition(to presentationStyle: MSMessagesAppPresentationStyle) {
if presentationStyle == .expanded {
if shouldPerformCreateSegue == true {
shouldPerformCreateSegue = false
performSegue(withIdentifier: "CreateStickerSegue", sender: theSender)//here is where I present the new viewController
} else {
searchBar.becomeFirstResponder()
searchBar.placeholder = nil
searchBar.showsCancelButton = true
searchBar.tintColor = UIColor.white
}
} else {
searchBar.showsCancelButton = false
}
print(topLayoutGuide.length) //This prints out 86
}
In the other modally presented view controller:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.view.addConstraint(navBar.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor))
print(topLayoutGuide.length) //This prints out 0
}
As a workaround I use UIPresentationController, which shifts the modal view controller by topLayoutGuide.length points:
class MyViewController: MSMessagesAppViewController {
private func presentModalViewController() {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .savedPhotosAlbum
imagePicker.modalPresentationStyle = .custom
imagePicker.transitioningDelegate = self
present(imagePicker, animated: true, completion: nil)
}
}
// MARK: - UIViewControllerTransitioningDelegate
extension MyViewController: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let vc = PresentationController(presentedViewController: presented, presenting: presenting)
// I really don't want to hardcode the value of topLayoutGuideLength here, but when the extension is in compact mode, topLayoutGuide.length returns 172.0.
vc.topLayoutGuideLength = topLayoutGuide.length > 100 ? 86.0 : topLayoutGuide.length
return vc
}
}
class PresentationController: UIPresentationController {
var topLayoutGuideLength: CGFloat = 0.0
override var frameOfPresentedViewInContainerView: CGRect {
guard let containerView = containerView else {
return super.frameOfPresentedViewInContainerView
}
return CGRect(x: 0, y: topLayoutGuideLength, width: containerView.bounds.width, height: containerView.bounds.height - topLayoutGuideLength)
}
}
The only problem is when you're calling presentModalViewController from compact mode, topLayoutGuide.length is 172.0 for unknown reason. So I had to hardcode a value for that case.
I believe this was known bug on previous iOS 10 beta. I had same issue and top and bottom layout guide works as I expect after I upgraded iOS version to latest.
I used a slightly varied version of Andrey's
class MyViewController: MSMessagesAppViewController {
private func presentModalViewController() {
let imagePicker = UIImagePickerController()
imagePicker.delegate = self
imagePicker.sourceType = .savedPhotosAlbum
imagePicker.modalPresentationStyle = .custom
imagePicker.transitioningDelegate = self
present(
imagePicker,
animated: true,
completion: nil
)
}
}
extension MyViewController: UIViewControllerTransitioningDelegate {
func presentationController(
forPresented presented: UIViewController,
presenting: UIViewController?,
source: UIViewController
) -> UIPresentationController? {
let vc = PresentationController(
presentedViewController: presented,
presenting: presenting
)
vc.framePresented = modalBoundaries.frame
return vc
}
}
class PresentationController: UIPresentationController {
var framePresented = CGRect.zero
override var frameOfPresentedViewInContainerView: CGRect {
return framePresented
}
}
modalBoundaries being a dummy UIView constrained (via XIB in my case) to respect any TopLayoutGuide length.

Resources