Posts

There is no single "current" view controller in iOS. There could be multiple view controllers on the screen at once (either nested or presented as popovers or modals). I think the closest equivalent to what you're asking for would be to start with the key window's root view controller and then find the "most-presented" (for lack of a better term) view controller. Something like this:

Thank you @adamkemp‌ it perfectly works. Now I realize I didn't know what exactly was I looking for because my knowledge on iOS is not deep enough. The translation from old native iOS to Xamarin is almost inmediate:

I am trying to login into fb so I need view controller to pass as a parameter. But its returning me null with a message
"The requested operation cannot be completed because the object has been garbage collected."

Your comment doesn't quite make sense. Where is that message coming from? The console? Which operation triggers that message? Where does the null come from? I need more details. What exact code did you run, and what specifically happened?

Hey Guys! I followed your example and it acting weird.. I was able to get it to work but I wanted to post this to help other people that might run into this as well.

So in the Xamarin IDE the inilisence does say that window.RootViewController is UiViewController but when I run the application and debug it says the type is UiNavigationController. I never seen this happen before where in debug it say's it's a different type. Anyhow casting it fixed the issue and works like this.

I think your use case seems different. You should not be blindly assuming the particular view controller hierarchy of the app. If the current root view controller isn't a navigation controller than that cast will throw an exception.

@ASyed, again, you have not provided enough information for me to help you. First of all, output in the console is not generally a problem. I don't see how that output relates at all to the code in question, and it doesn't seem like something you should be concerned about.

The way this works is it starts with the RootViewController and looks for the "most presented" view controller (the one at the end of the chain of PresentedViewControllers). window.RootViewController.PresentedViewController is null then that's not a problem. That just means you should present your new view controller on window.RootViewController. At the end of the while loop the vc variable should not be null, and whatever value it has is the view controller you should present from.

So given that explanation, can you please give more details about what exactly is going wrong when you try this? Does the presentation not work? Is it crashing? Which line is giving the problem?

Giving me application output is useless unless you can demonstrate that it's relevant and tell me which line produced the output. I want to know what the app did.

@adamkemp var window= UIApplication.SharedApplication.KeyWindow; var vc = window.RootViewController; while (vc.PresentedViewController != null) { vc = vc.PresentedViewController; }
In this code as we are using while loop skips without looping because vc.PresentedViewController == null. So we can never get to the code inside the loop. While loop jumps out of the loop when the condition is false. As vc.PresentedViewController != null is false it never goes inside the loop. If I try

while (vc.PresentedViewController == null) { vc = vc.PresentedViewController; }
then it never comes out of the loop as its keep searching for vc.PresentedViewController to be not null but as it never happens it keeps looping.

In this code as we are using while loop skips without looping because vc.PresentedViewController == null. So we can never get to the code inside the loop. While loop jumps out of the loop when the condition is false. As vc.PresentedViewController != null is false it never goes inside the loop.

That is exactly what is supposed to happen if the RootViewController is not actually presenting anything. That is the correct behavior. The result in this case is that vc == window.RootViewController (which is notnull). The loop will only be entered if you are currently presenting something already, but if you're not presenting anything then it will skip the loop, which is not a problem.

There should be no problem with you passing vc to the code you pasted. What happens when you call that code with the vc you get from my code?

@adamkemp said:
There is no single "current" view controller in iOS. There could be multiple view controllers on the screen at once (either nested or presented as popovers or modals). I think the closest equivalent to what you're asking for would be to start with the key window's root view controller and then find the "most-presented" (for lack of a better term) view controller. Something like this:

i had scenario with modal and @adamkemp's excellent approach was returning the underlying view vs the modal... this wound up working for me:
(using System.Linq)
Application.SharedApplication.Windows.OrderByDescending(w=>w.WindowLevel).First().RootViewController;

I'm curious what kind of modal that was. Most modals go in the same window so usually you don't have to worry about multiple windows. Maybe if you give us some more information it will help people decide whether they need the more complicated approach or not.

thanks for your interest adam... i've attempted to post a response including specific code but it seems it was blocked (so far?)... as long as this site practices that kind of sensoring i guess we'll just have to live with incomplete information

@SteveAndrews, as mentioned in my first response above, you can't assume that the root view controller is capable of presenting something at any given time. A view controller can only "present" one other view controller on top of it, and a modal counts. So if you want to present a new view controller you have to find the "most-presented" view controller (my own terminology), which means walking the linked list path from the root view controller using the PresentedViewController property (see the code above). Once you find a view controller that is not presenting another view controller you can then use that one to present your new view controller.

That while loop above, looking for the most-presented, was the technique I mentioned that crashes the app. I wasn't specific in my post and I apologize for that. Even when I wrap it in a try-catch, the app is exited without anything written to the application output window.

Can you step through it and figure out which line is crashing? My guess is that the crash happens on the PresentViewController line, which means that you likely would have crashed without the loop if it had actually tried to do the presentation instead of skipping it.

Update: I moved the code to the actual page of the modal to see if that made any difference. It now allows me to see the error in the output window:

Xamarin.iOS: Received unhandled ObjectiveC exception: NSGenericException Your application has presented a UIActivityViewController (<UIActivityViewController: 0x10339f0a0>). In its current trait environment, the modalPresentationStyle of a UIActivityViewController with this style is UIModalPresentationPopover. You must provide location information for this popover through the view controller's popoverPresentationController. You must provide either a sourceView and sourceRect or a barButtonItem. If this information is not known when you present the view controller, you may provide it in the UIPopoverPresentationControllerDelegate method -prepareForPopoverPresentation.

That makes much more sense. You would have gotten the same issue had you not used the while loop (i.e., if you weren't presenting a modal). Use the PopoverPresentationController property of the activity view controller to set up the source view/source rect or bar button item to present from.

thanks for chiming back @adamkemp... after a lot more debug inspection, i found both your and my approach were both equally susceptible to the issue i was having... as a small aside, i'd propose my approach is less complex -- at least less verbose -- but that's a matter of personal taste of course.

i'll try to elaborate on the "modal" flow i worked out because going by other posts it seems like a lot of us newbies run into issues trying to have a masterDetailPage+navigationPage with an "inescapable" initial login page... and modal seems like a natural initial choice to achieve "inescapable"

in my case i wanted to perform login via Auth0 service which, typical to these auth contexts, pops it's own (web) UI in order to manage the callback/token flow and this auth component requires a native UI "handle" (iOS = ViewController, Android=Activity)... hence why i wound up on this thread...

anyway, the crux of my hangup was that in the lifecycle sequence of app initialization, the native UI windows weren't yet "registered" to be available to the auth component... specifically, Application.SharedApplication.Windows.Length == 0... after tracing and tracing and trying tons of different approaches i came to a guess that i am thanking stars is working -

wrap the auth api call (from loginPage) with Device.BeginInvokeOnMainThread()... presumably this frees the current thread to finalize app initialization stack to where UIApplication.Windows is then populated when other thread comes back to reference it.

at this point, i'm actually not using modal... i fire up app with only loginPage (hence inherently inescapable) and then upon successful login, go ahead and navigate to MasterDetail+Navigation+Detail as the lasting navigational structure...

i happen to be using the Prism+Unity and since it's docs are pretty light i'm a little lost on ideal application of modal under that framework but am very thankful to finally have a working solution in the following code:

App.xaml.cs

protected override void OnInitialized()
{
InitializeComponent();
NavigationService.NavigateAsync($"{nameof(Login)}",
new NavigationParameters { {"afterLogin", $"{nameof(MainMasterDetail)}/{nameof(MainNavContainer)}/{nameof(FirstLanding)}" } });
//nugget: while developing, if app crashes on start without good exception stack, temporarily add .Wait() to bubble exception back to main thread and show up in the Output > Debug window
}

thanks for chiming back @adamkemp... after a lot more debug inspection, i found both your and my approach were both equally susceptible to the issue i was having... as a small aside, i'd propose my approach is less complex -- at least less verbose -- but that's a matter of personal taste of course.

i'll try to elaborate on the "modal" flow i worked out because going by other posts it seems like a lot of us newbies run into issues trying to have a masterDetailPage+navigationPage with an "inescapable" initial login page... and modal seems like a natural initial choice to achieve "inescapable"

in my case i wanted to perform login via Auth0 service which, typical to these auth contexts, pops it's own (web) UI in order to manage the callback/token flow and this auth component requires a native UI "handle" (iOS = ViewController, Android=Activity)... hence why i wound up on this thread...

anyway, the crux of my hangup was that in the lifecycle sequence of app initialization, the native UI windows weren't yet "registered" to be available to the auth component... specifically, Application.SharedApplication.Windows.Length == 0... after tracing and tracing and trying tons of different approaches i came to a guess that i am thanking stars is working -

wrap the auth api call (from loginPage) with Device.BeginInvokeOnMainThread()... presumably this frees the current thread to finalize app initialization stack to where UIApplication.Windows is then populated when other thread comes back to reference it.

at this point, i'm actually not using modal... i fire up app with only loginPage (hence inherently inescapable) and then upon successful login, go ahead and navigate to MasterDetail+Navigation+Detail as the lasting navigational structure...

i happen to be using the Prism+Unity and since it's docs are pretty light i'm a little lost on ideal application of modal under that framework but am very thankful to finally have a working solution in the following code:

App.xaml.cs

protected override void OnInitialized()
{
InitializeComponent();
NavigationService.NavigateAsync($"{nameof(Login)}",
new NavigationParameters { {"afterLogin", $"{nameof(MainMasterDetail)}/{nameof(MainNavContainer)}/{nameof(FirstLanding)}" } });
//nugget: while developing, if app crashes on start without good exception stack, temporarily add .Wait() to bubble exception back to main thread and show up in the Output > Debug window
}

@adamkemp said:
There is no single "current" view controller in iOS. There could be multiple view controllers on the screen at once (either nested or presented as popovers or modals). I think the closest equivalent to what you're asking for would be to start with the key window's root view controller and then find the "most-presented" (for lack of a better term) view controller. Something like this:

UIAlertControllers are UIViewControllers, which are not the same as UIViews. Your code tries to find a view that is a UIAlertController, but that can't possibly work because an alert controller is not a view. (Aside: you're also only searching top-level views, but view hierarchies are trees so you would have to search every branch recursively if you were looking for a view.)

View controllers have a hierarchy separate from views, and that's what you need to look through. The code I had above looks through the hierarchy of presented view controllers, which fortunately is just a linked list. I'm not sure why that wouldn't find your alert controller too. Try this: