How to acces the current view UIViewController from an external service

TotemikaTotemika Rosana RuizESMember ✭✭
edited September 2014 in Xamarin.Forms

I'm creating a Service in Xamarin.Forms that needs to open a new UI.

For Android I'm using:

        private Context _activity;
        _activity.StartActivity(_authenticator.GetUI(_activity));

For iOS I need to access the current view to use the method PresentViewController();
Is there an equivalent to the Context object for iOS?

Thank you

Posts

  • TotemikaTotemika Rosana Ruiz ESMember ✭✭

    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:

    Get the current keyWindow:

    UIWindow *window = [UIApplication sharedApplication].keyWindow;

    Get its rootViewController:

    UIViewController *rootViewController = window.rootViewController;

    (Found on: http://stackoverflow.com/questions/5968703/how-to-find-root-uiviewcontroller)

    Thank you so much again

  • SmathsSmaths Eric Wroblewski USMember ✭✭

    Awesome! This works.

    Thanks for the info.

  • tewaritewari Ash Tewari USMember, University

    Works like a charm! Thanks.

  • tewaritewari Ash Tewari USMember, University

    Works like a charm! Thanks.

  • JagtapMaheshJagtapMahesh Jagtap Mahesh USMember

    It works great. Thanks.

  • AmjadAzizi10AmjadAzizi10 Amjad Azizi USMember ✭✭

    Thanks @adamkemp

  • ASyedASyed Azhar Ali Syed USMember ✭✭

    @adamkemp

    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."

    Can you please advice how to resolve it ?

  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    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?

  • ThomasDepoleThomasDepole Thomas Depole USMember ✭✭

    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.

    var window = UIApplication.SharedApplication.KeyWindow;
    var controller = (UINavigationController)window.RootViewController;
    if (controller != null && controller.TopViewController is MapViewController)
    ((MapViewController)controller.TopViewController).HandleNotification();

  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    UINavigationController is a subclass of UIViewController.

    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.

    What are you actually trying to do?

  • ASyedASyed Azhar Ali Syed USMember ✭✭

    @adamkemp

    When I tried to use the piece of code you suggested I am getting the vc.PresentedViewController to be null and in the application output I can see the following error..

    -canOpenURL: failed for URL: "fbauth2:/" - error: "(null)"

    Can you suggest a way to resolve this ?

  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    @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.

    For context, this is the code that I suggested earlier:

    var window= UIApplication.SharedApplication.KeyWindow;
    var vc = window.RootViewController;
    while (vc.PresentedViewController != null)
    {
        vc = vc.PresentedViewController;
    }
    

    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.

  • ASyedASyed Azhar Ali Syed USMember ✭✭

    @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.

    I want to use vc value in :

    LoginManager.LogInWithReadPermissions (permissions, vc, (LoginManagerLoginResult result, NSError error) => { if(error.IsNotNull ()) { tcs.SetException (new IosErrorException(error)); } else { tcs.SetResult (result); } });

    but because from the code vc is null I am not vc is null in LoginManager.LogInWithReadPermissions function and i am unable to login fb

  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    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 not null). 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?

  • ASyedASyed Azhar Ali Syed USMember ✭✭

    When this happens, in the application output I am getting the following error:

    canOpenURL: failed for URL: "fbauth2:/" - error: "(null)" but I am able to login. I was wondering if this error displayed on application output might be critical.

  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    Don't worry about application output. If it works then it's fine.

  • ASyedASyed Azhar Ali Syed USMember ✭✭

    Thank you..

  • OscarRodriguezOscarRodriguez Oscar Rodriguez USMember

    @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:

    var window= UIApplication.SharedApplication.KeyWindow;
    var vc = window..RootViewController;
    while (vc.PresentedViewController != null)
    {
        vc = vc.PresentedViewController;
    }
    // Present on vc.
    

    This works very well !!!

  • BrentAndersonBrentAnderson Brent Anderson USMember ✭✭

    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;

  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    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.

  • BrentAndersonBrentAnderson Brent Anderson USMember ✭✭

    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

  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    It's not censorship, but your post may have been automatically rejected for looking like spam. I marked you as verified so try your post again.

  • SteveAndrewsSteveAndrews Steve Andrews USMember
    edited October 2016

    I've been dealing with a different, but similar issue. I'm trying to provide a "share sheet" for the user.

    I'm using Xamarin.Forms and am on a modal page from a Navigation.PushModalAsync. The following code is being run in my iOS-specific class.

        var activityController = new UIActivityViewController (
            new Foundation.NSObject [] { UIActivity.FromObject ("This is my message") }, 
            null
        );
    
        activityController.SetValueForKey (
            Foundation.NSObject.FromObject ("This is my title"), 
            new Foundation.NSString ("subject")
        );
    
        UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController (
            activityController, 
            true, 
            null
        );
    

    The application doesn't crash but I do get the following in my output console:

    Warning: Attempt to present <UIActivityViewController: 0x10e1059f0> on <Xamarin_Forms_Platform_iOS_PlatformRenderer: 0x103662be0> whose view is not in the window hierarchy!

    I've tried the techniques above but they crash the app. All of this worked fine before the other developers shifted to this modal presentation method. Any ideas?

  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    @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.

  • SteveAndrewsSteveAndrews Steve Andrews USMember

    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.

    try {
    
        var activityController = new UIActivityViewController (
                new Foundation.NSObject [] { UIActivity.FromObject ("This is my message") }, 
                null
        );
    
        activityController.SetValueForKey (
                Foundation.NSObject.FromObject ("This is my title"), 
                new Foundation.NSString ("subject")
        );
    
    
        var topController = UIApplication.SharedApplication.KeyWindow.RootViewController;
        while (topController.PresentedViewController != null) {
            topController = topController.PresentedViewController;
        }
        topController.PresentViewController (activityController, true, null);
    
    } catch (Exception ex) {
        Console.WriteLine (ex.Message);
    }
    
  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    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.

  • SteveAndrewsSteveAndrews Steve Andrews USMember

    It executed the loop twice before falling out, then running the PresentViewController line. That's where the wheels fall off the bus.

  • SteveAndrewsSteveAndrews Steve Andrews USMember

    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.

  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    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.

  • SteveAndrewsSteveAndrews Steve Andrews USMember

    The fix for my modal case, was to add the following. It doesn't put the popover where the gesture initiated it but it works without error and that's a big improvement.

                    UIKit.UIView view = this.CreateViewController ().View;
                    activityController.PopoverPresentationController.SourceRect = view.Bounds;
                    activityController.PopoverPresentationController.SourceView = view;
    
  • BrentAndersonBrentAnderson Brent Anderson USMember ✭✭

    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
        }
    

    LoginViewModel.cs (note applying Device.BeginInvokeOnMainThread() herein)

    namespace HfcSpark.ViewModels 
    {
      class LoginViewModel : BaseViewModel
      {
        public DelegateCommand LoginCommand { get; }
    
        public LoginViewModel(INavigationService navigationService, IAuth0Client auth0)
        {
          LoginCommand = new DelegateCommand(async () => 
          {
            try
            {
              await auth0.LoginAsync();
              if (auth0.IsLoggedIn) await navigationService.NavigateAsync(_afterLogin?.ToString());
            }
            catch (TaskCanceledException){} //if user cancels the Auth0 login UI
          });
        }
    
        private object _afterLogin;
        public override void OnNavigatedTo(NavigationParameters parameters)
        {
          parameters.TryGetValue("afterLogin", out _afterLogin);
    
          Device.BeginInvokeOnMainThread(() => //<=== here's the beef
          {
            LoginCommand.Execute();
          });
        }
      }
    }
    

    Login.xaml

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="HfcSpark.Views.Login">
    
      <StackLayout Margin="20" Orientation="Vertical">
        <Label Text="Login page"/>
        <Button Margin="0,20,0,0" Text="Please login to continue" Command="{Binding LoginCommand}" />
      </StackLayout>
    </ContentPage>
    

    IAuth0.cs

    using System.Diagnostics;
    using Prism.Mvvm;
    
    namespace HfcSpark.Services
    {
      public interface IAuth0Client
      {
        System.Threading.Tasks.Task<Auth0UserX> LoginAsync(string connection = "", bool withRefreshToken = false, string scope = "openid", string title = null);
        void Logout();
        bool IsLoggedIn { get; }
        Auth0UserX CurrentUser { get;  }
      }
    
      public abstract class AuthClientBase : BindableBase
      {
        private Auth0UserX _currentUser;
        public Auth0UserX CurrentUser { get
          {
            return _currentUser;} protected set { SetProperty(ref _currentUser, value); } }
        public bool IsLoggedIn => CurrentUser != null;
      }
    
      //seems pointless to interface since a test can readily manipulate a flat DTO like this
      public class Auth0UserX
      {
        public string Auth0AccessToken { get; set; }
        public string IdToken { get; set; }
        public Newtonsoft.Json.Linq.JObject Profile { get; set; }
        public string RefreshToken { get; set; }
        public string FirstName => Profile?["given_name"]?.ToString();
        public string Image => Profile?["picture"]?.ToString();
      }
    
    }
    

    Auth0iOS.cs (note obtaining RootViewController herein)

    namespace HfcSpark.Services
    {
      public class Auth0ClientIos : AuthClientBase, IAuth0Client
      {
        private  readonly Auth0.SDK.Auth0Client _platformAuth0;
    
        public void Logout()
        {
          _platformAuth0.Logout();
        }
        public Auth0ClientIos(string domain, string clientId)
        {
          _platformAuth0 = new Auth0.SDK.Auth0Client(domain, clientId);
        }
    
        public async System.Threading.Tasks.Task<Auth0UserX> LoginAsync(string connection = "", bool withRefreshToken = false, string scope = "openid", string title = null)
        {
          var user = await _platformAuth0.LoginAsync( UIApplication.SharedApplication.Windows.OrderByDescending(w => w.WindowLevel).First().RootViewController );
          CurrentUser = new Auth0UserX { Auth0AccessToken = user.Auth0AccessToken, Profile = user.Profile, IdToken = user.IdToken, RefreshToken = user.RefreshToken };
          return CurrentUser;
        }
    
      }
    }
    
  • BrentAndersonBrentAnderson Brent Anderson USMember ✭✭

    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
        }
    

    LoginViewModel.cs (note applying Device.BeginInvokeOnMainThread() herein)

    namespace HfcSpark.ViewModels 
    {
      class LoginViewModel : BaseViewModel
      {
        public DelegateCommand LoginCommand { get; }
    
        public LoginViewModel(INavigationService navigationService, IAuth0Client auth0)
        {
          LoginCommand = new DelegateCommand(async () => 
          {
            try
            {
              await auth0.LoginAsync();
              if (auth0.IsLoggedIn) await navigationService.NavigateAsync(_afterLogin?.ToString());
            }
            catch (TaskCanceledException){} //if user cancels the Auth0 login UI
          });
        }
    
        private object _afterLogin;
        public override void OnNavigatedTo(NavigationParameters parameters)
        {
          parameters.TryGetValue("afterLogin", out _afterLogin);
    
          Device.BeginInvokeOnMainThread(() => //<=== here's the beef
          {
            LoginCommand.Execute();
          });
        }
      }
    }
    

    Login.xaml

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 x:Class="HfcSpark.Views.Login">
    
      <StackLayout Margin="20" Orientation="Vertical">
        <Label Text="Login page"/>
        <Button Margin="0,20,0,0" Text="Please login to continue" Command="{Binding LoginCommand}" />
      </StackLayout>
    </ContentPage>
    

    IAuth0.cs

    using System.Diagnostics;
    using Prism.Mvvm;
    
    namespace HfcSpark.Services
    {
      public interface IAuth0Client
      {
        System.Threading.Tasks.Task<Auth0UserX> LoginAsync(string connection = "", bool withRefreshToken = false, string scope = "openid", string title = null);
        void Logout();
        bool IsLoggedIn { get; }
        Auth0UserX CurrentUser { get;  }
      }
    
      public abstract class AuthClientBase : BindableBase
      {
        private Auth0UserX _currentUser;
        public Auth0UserX CurrentUser { get
          {
            return _currentUser;} protected set { SetProperty(ref _currentUser, value); } }
        public bool IsLoggedIn => CurrentUser != null;
      }
    
      //seems pointless to interface since a test can readily manipulate a flat DTO like this
      public class Auth0UserX
      {
        public string Auth0AccessToken { get; set; }
        public string IdToken { get; set; }
        public Newtonsoft.Json.Linq.JObject Profile { get; set; }
        public string RefreshToken { get; set; }
        public string FirstName => Profile?["given_name"]?.ToString();
        public string Image => Profile?["picture"]?.ToString();
      }
    
    }
    

    Auth0iOS.cs (note obtaining RootViewController herein)

    namespace HfcSpark.Services
    {
      public class Auth0ClientIos : AuthClientBase, IAuth0Client
      {
        private  readonly Auth0.SDK.Auth0Client _platformAuth0;
    
        public void Logout()
        {
          _platformAuth0.Logout();
        }
        public Auth0ClientIos(string domain, string clientId)
        {
          _platformAuth0 = new Auth0.SDK.Auth0Client(domain, clientId);
        }
    
        public async System.Threading.Tasks.Task<Auth0UserX> LoginAsync(string connection = "", bool withRefreshToken = false, string scope = "openid", string title = null)
        {
          var user = await _platformAuth0.LoginAsync( UIApplication.SharedApplication.Windows.OrderByDescending(w => w.WindowLevel).First().RootViewController );
          CurrentUser = new Auth0UserX { Auth0AccessToken = user.Auth0AccessToken, Profile = user.Profile, IdToken = user.IdToken, RefreshToken = user.RefreshToken };
          return CurrentUser;
        }
    
      }
    }
    
  • UdaraAlwisUdaraAlwis Udara Alwis LKMember ✭✭
    edited January 26

    Although do not forget to access it via the UI thread :) otherwise it will throw the UI thread access exception. Thanks @adamkemp for the initial solution.

    UIApplication.SharedApplication.InvokeOnMainThread(() =>
    {
        var window = UIApplication.SharedApplication.KeyWindow;
        var vc = window.RootViewController;
        while (vc.PresentedViewController != null)
        {
            vc = vc.PresentedViewController;
        }
    });
    

    Cheers!

  • SinanUarSinanUar Sinan Uçar USMember

    @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:

    var window= UIApplication.SharedApplication.KeyWindow;
    var vc = window.RootViewController;
    while (vc.PresentedViewController != null)
    {
        vc = vc.PresentedViewController;
    }
    // Present on vc.
    

    It works great.. thx

  • Prajakta.ShindePrajakta.Shinde Prajakta Shinde INUniversity ✭✭
    edited April 12

    I am getting window value null in
    var window = UIApplication.SharedApplication.KeyWindow;

    and getting the error as
    You must set the rootViewController property of GADBannerView: 0x18478870; frame = (0 974; 768 90); clipsToBounds = YES; layer = before loading a request.

    Please help.

  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    You are probably running this code before the app has finished launching.

  • kalyanReddykalyanReddy kalyan Reddy USMember ✭✭
    edited April 25

    @adamkemp I have a different scenario. Working on closing the UIAlertViewController when app is going background.

    Not sure how we solve this xamarin forms

    public override void DidEnterBackground (UIApplication uiApplication) { var subViews = UIApplication.SharedApplication.KeyWindow.Subviews; foreach (var view in subViews) { if (view.IsKindOfClass(UIAlertController)) { // should convert UIAlertController to Objc class //dismiss uicontroller } } base.DidEnterBackground (uiApplication); }

  • adamkempadamkemp Adam Kemp USInsider, Developer Group Leader mod

    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:

    public override void DidEnterBackground (UIApplication uiApplication)
    {
        var window= UIApplication.SharedApplication.KeyWindow;
        var vc = window.RootViewController;
        while (vc.PresentedViewController != null)
        {
            vc = vc.PresentedViewController;
        }
        UIAlertController alertController = vc as UIAlertController;
        if (alertController != null)
        {
            // dismiss here
        }
    }
    

    If that doesn't work then you need to be a lot more specific about how this alert is presented and what happens when you run the code above (like what is vc after the loop?).

  • VijayKumar.0628VijayKumar.0628 Vijay Kumar USMember ✭✭
Sign In or Register to comment.