Trouble dismissing alert viewcontroller presented with DismissViewController

Jacob D 1 Reputation point
2020-11-19T01:25:51.247+00:00

So in my Xamarin.iOS project, I have an abstract view class (let's call it A) with code that looks like this:
PresentViewController(_alertController, false, null);

where:
_alertController = UIAlertController.Create(message, null, UIAlertControllerStyle.Alert);

This is supposed to surface a "Loading..." alert, that should only disappear once the service call that is tied to the action taken returns. At this point, the alert should be dismissed with the code:
_alertController.DismissViewController(false, null);

However, I noticed that this code doesn't always work. In particular, there are instances in which the service call gets back and the program tries to dismiss the alertController before it is even presented and added to the parent viewcontroller.

There are some child classes of A for which this problem never happens, and one child class (let's call it C, where C: A) in which we almost always run into this problem. I don't know if the problem is related to different class behaviors, but just figured I'd provide this information in case useful.

I've also noticed that in the instances where the problem doesn't occur in view class C, the view has the following fields (which are null when the problem occurs):
ChildViewControllers {UIKit.UIViewController[1]}
ModalViewController {UIKit.UIAlertController}
NavigationController {UIKit.UINavigationController}
PresentedViewController {UIKit.UIAlertController}
PresentingViewController {UIKit.UINavigationController}

I would really appreciate any ideas for how I can address the issue. I apologize if the solution is obvious - I'm very new to iOS development and have been staring at the problem for too long, so I'm low on ideas.
Thank you

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,362 questions
0 comments No comments
{count} votes

2 answers

Sort by: Most helpful
  1. LandLu-MSFT 21 Reputation points
    2020-11-19T03:06:34.453+00:00

    Hello,

    Welcome to Microsoft Q&A!

    We could check current view controller's presented controller by using:

    var presentedVC = PresentedViewController;  
    

    If it equals to your _alertController, it means the _alertController has been presented, and then we could dismiss it by

    _alertController.DismissViewController(true, null);  
     
    
     
    

    Thank you.


    If the response is helpful, please click "Accept Answer" and upvote it.
    Note: Please follow the steps in our documentation to enable e-mail notifications if you want to receive the related email notification for this thread.

    0 comments No comments

  2. Jacob D 1 Reputation point
    2020-11-21T19:23:28.4+00:00

    Thank you for your answer @LandLu-MSFT . Sorry - my original question had a typo so it was misleading. I updated it. That is the roughly the strategy that I'm currently following. However, it doesn't work.
    The problem as I understand it (from what I've seen debugging the code) is that even after the PresentViewController method is called, the alert controller takes a significant amount of time to display and registered as being the presented view controller. This delay seems to be at fault for the error. This is because I have an event handler function that looks like this:

    private void HandleHttpResponseEvent(Event e)  
            {  
                View.HideProgress();  
    			...  
    	}  
    
    	  
    

    But the HideProgress call happens before the view controller is presented. The view controller is presented after the HideProgress function returns, leaving us stuck with the "Loading..." view.

    I tried to implement a fix so that the HideProgress method waits for the alert controller to be presented:

    It looks like so:

       private Task<object> alertIsPresented;  
      
        public virtual void ShowProgress(string message = "Loading...")  
        {  
        InvokeOnMainThread(() =>  
        {  
            alertController = UIAlertController.Create(message, null, UIAlertControllerStyle.Alert);  
            var tcs = new TaskCompletionSource<object>();  
            alertIsPresented = tcs.Task;  
            Action alertIsPresentedAction = delegate () { tcs.SetResult(null); };  
            PresentViewController(_alertController, false, alertIsPresentedAction);  
       }  
    }  
    
         public virtual void HideProgress()  
         {  
                    InvokeOnMainThread(async () =>  
                    {  
                        if (_alertController != null)  
                        {  
                            await alertIsPresented;  
          
                            _alertController.DismissViewController(false, null);  
                         }  
                     }  
         }  
    

    So now the dismissal of the loading view is contingent on the event occurring and the view being presented.
    I would appreciate feedback from others on how robust this solution is/if it has any potential issues. So far it seems to be fine

    Thanks

    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.