Presentation of UIHostingController glitches with .overCurrentContext modal presentation style - ios

I am trying to display a UIHostingController which shows my SwiftUI Popup on screen - however, the presentation glitches and the presented view controller is somewhere stuck in the topmost left corner (see picture, the glitching hosting controller is inside the red circle).
This only occurs if I display another View controller before my hosting controller. So, if nothing was presented before my hosting controller, everything works fine.
Things also work fine, if I set my UIHostingController's modalPresentationStyle = .formSheet - however, that's not a solution for me since I need the behavior of .overCurrentContext for my use case.
So, in short, I do experience a glitch when:
an arbitrary view controller is presented (using self.present(...)) at some point
before my UIHostingViewController is presented.
I do not get that glitch when:
I only display the UIHostingViewController
Code
I receive the described behavior using the following code:
// First, I display an ActivitySheet
let ac = UIActivityViewController(activityItems: [someItems], applicationActivities: nil)
self.present(ac, animated: true)
// then I present my ViewController using overCurrentContext, after the ActivityViewController was dismissed
let myPopupVC = MyPopupVC()
myPopupVC.isModalInPresentation = true
myPopupVC.modalPresentationStyle = .overCurrentContext // error would not occur if this was .formSheet, but I need .overCurrentContext for my use case.
myPopupVC.modalTransitionStyle = .crossDissolve
self.definesPresentationContext = true
self.present(self, animated: true)
Both modal presentations are the result of button clicks in my interface. The UIActivityViewController is displayed when a share button is clicked, and the popup is displayed when the leave button is pressed (to confirm the user is sure they want to do that).
What I assume
My first thought was that this could have something to do with the UIHostingController being presented by the dismissed activity sheet, however, that wouldn't make sense since it is freed from memory according to Apple's documentation on view controller dismissal (chapter "Dismissing a presented View Controller").
I therefore don't understand why this is happening.
Does anyone know why this behavior arises and, most importantly, how I can fix it using .overCurrentContext as my modal presentation style?

The mistake was that I set translatesAutoResizingMaskIntoConstraints to false, which resulted in the UIHostingController's view to lack information needed to position itself correctly.
Therefore, make sure that translatesAutoResizingMaskIntoConstraints remains true if you modally present a UIHostingController.

Related

Transition from one modal VC to another without delay swift

I have a basic scenario:
I present a VC modally using self.present(, animated:, completion:).
Sometimes due to interactions in this modal VC i need to close one modal and open another one.
So i do the following:
weak var presenter = self.presentingViewController
let newVc = UIViewController()
presenter?.dismiss(animated: true, completion: {
presenter?.present(newVc, animated: true, completion: nil)
})
This works but there is the annoying delay when switching the VC's when user sees the original presenter and can try to interact with it (to open other modals...).
I tried setting animated: false but that doesn't seem to work :/
I can't really switch to UINavigationController model for this because the modals i am presenting themselves are Page View Controllers and have the whole hierarchy of dependent views; the user is never going 'back'; so i'd really like to just present the new modal as quickly as possible...
Update My question is not about how to control or choose the animation. My questions is about having no delay between the modals.
The built-in view controller architecture that switches views with no transition is the tab bar controller. So just turn your view controller into a tab bar controller — with no visible tab bar! To change to the other view controller just change tabs (in code). The change is instant.
This screencast makes it clear that this works as described. We present a view controller (yellow). Then we switch back and forth between two view controllers (green and yellow) as the presented view controllers, instantly. Finally, we dismiss whichever one (green or yellow) is showing. I'm doing it all with simple buttons but that's just for the demo; obviously you could do this however you like. It's the architecture that's the important thing.
I can think only of solutions which would require you to handle the animations yourself
create custom modal transition using UIViewControllerTransitionCoordinator
add your controllers to container views as suggested by #muhammed-gül
present newVC over self and dismiss all presented controllers when you're done
And just a tip, you don't always need to wait for the dismiss completion closure, you can call this and it usually works, but still the underlying viewController is visible.
dismiss(animated: true)
present(newVC, animated: true)

Benefit of using .fullScreen presentation style in compare to .overFullScreen presentation style in ios

I am curious to know that what is/are the actual benefit(s) (with some example) we get if we use .fullScreen versus .overFullScreen presentation style while presenting a view controller modally over rootViewController in an iOS app.
I read using .overFullScreen will unnecessarily show the content below it if presentedViewController has transparency, but if I use .fullScreen then also there will be a grey screen visible behind transparency which won't look good. So I am not sure how this is beneficial.
.fullScreen: the views belonging to the presenting view controller are removed after the presentation completes.
.overFullScreen: the view beneath the presented are not removed from the view hierarchy when the presentation finishes. So if the presented view controller does not fill the screen with opaque content, the underlying content shows through.
This is the actual difference between them.
I hope it will help you.
when back .fullScreen will call viewDidAppear
.overFullScreen won't call it.

Can't add view infront of modal controller's view

I have a modally presented view controller and I want to push a modal view on top of it. This should be simple to do, but I've missed something :)
The modally presented view controller doesn't cover the whole screen (it uses custom presentation) so I can't just add my view on top of that. But when I try to add my view to either the presenting view controller, or just onto UIApplication.shared.keyWindow.rootViewController.view I get a very strange result.
I've tried this using the default modal presentation an it still behaves in the same way, so I don't believe it's my custom presentation causing this.
Here is the exploded view from Xcode - with the overlay at the correct position front and center.
And here is the simulator at the time this interface was snapshotted.
Does anyone know why the alert view (which is clearly at the top of the stack looking in Xcode) doesn't appear?
NB: I'm pretty confident that the exploded view in Xcode is correct because it matches up with the output from po [[[[[UIApplication sharedApplication] keyWindow] rootViewController] view] recursiveDescription]
I think you should try to present some vc in your current and in other view controllers first.
... // your view controller
present(/* your alert view controller */, animated: true, completion: nil)
For example, it should show a empty vc like below. See if you can present any vc normally. If yes, it might be your alert view controller problem.
... // your view controller
let vc = UIViewController()
present(vc, animated: true, completion: nil)
... // presented a black screen, it works

Dismissing modally presented ViewController always throws me back to root

I've had this issue for months with multiple views, both Apple provided like ImagePicker and VCs from storyboard.
I believe that it has something to do with the underlying views we have both a tab bar controller and navigation controller in most views.
Strange thing is using some open source views from pods does not cause this bug.
So I'm two views deep on a navigation controller and present another view modally on top with present(vc, animated: true, completion: {})
Works like a charm, now dismissing that view with dismiss(animated: true, completion: nil) throws me back all the way to the initial view or root view of the navigation controller, had both happen before, depending on the presented view.
Update:
Build a sample project trying to reproduce the behavior but failed. Drew a reduced diagram to better explain the current bug behavior.
Also noticed that if I'm invoking the post view one step earlier in the Fandom view it works as expected.
In my case i am using UITabBarController, and I wrote code in viewWillAppear of UITabBarController
self.selectedIndex = 2
so when i present any thing from any controller whose parent is UITabBarController and when i dismiss that it automatically open third tab of UITabBarController.
Maybe you explicitly wrote any code to select specific index of TabBar.
Maybe this is useful for you or anyone else.

Presenting Modal ViewController from (Table)ViewController inside a NavigationController inside a TabBarController

When I try presenting the modal controller right from the table view controller (could be a normal view controller as well), it appears behind the tab bar and looks quite ugly as I'm using a blur effect on it. I am using a navigation controller because I need to have a bar at the top and, after research, found that's the best way to do it.
I have found that doing something like:
self.parent?.parent?.present(ModelViewController(), animated: true, completion: nil)
when wanting to present the modal controller works. However, I imagine this isn't very safe. What is the correct way of doing this?
In order for the ModalViewController to present in front of the tab bar, its modalPresentationStyle has to be set to overFullScreen. So for instance when initializing the ModalViewController:
self.modalPresentationStyle = .overFullScreen (Swift 3 syntax)

Resources