Sorin Comanescu
Sorin Comanescu

Reputation: 4867

Wait for PresentModalViewController to finish (monotouch)

I would like to wait for a controller being displayed with PresentModalViewController() to finish its job before resuming execution (like modal dialogs in WinForms). How can this be done with monotouch?

I know there is a similar question on SO but the answer is for Objective-C and, frankly, I don't get it.

Many thanks.


EDIT

Here's the first setup I tried and which didn't seem to work:

  1. Create a new Project (Single View Application);
  2. Add two controllers (iPhone View Controller): FirstController and SecondController; the first controller overrides DismissModalViewControllerAnimated and fires a OnFirstFinished event just after being dismissed;
  3. In the main controller:

...

public partial class TestModalViewController : UIViewController
{
    private UIButton button;
    private FirstController first;
    private SecondController second;

    public override void ViewDidLoad ()
    {
      base.ViewDidLoad ();

      button = UIButton.FromType(UIButtonType.RoundedRect);
      button.Frame = new RectangleF(0, 0, 100, 50);
      button.SetTitle("Test", UIControlState.Normal);
      button.TouchUpInside += PresentFirstController;

      View.Add(button);

      PresentFirstController(null, null);
    }

    void PresentFirstController (object sender, EventArgs e)
    {
      bool firstFinished = false;

      first = new FirstController();

      first.OnFirstFinished += delegate(object s, EventArgs args) {
        firstFinished = true;
      };

      this.PresentModalViewController(first, true);

      do
      {
        NSRunLoop.Current.RunUntil (NSDate.FromTimeIntervalSinceNow (0.5));
      } while (!firstFinished);

      second = new SecondController();
      this.PresentModalViewController(second, true);

    }

    // ...
}
  1. In FirstController:

...

public override void DismissModalViewControllerAnimated (bool animated)
{
    base.DismissModalViewControllerAnimated (animated);

    if(null != OnFirstFinished)
    {
        OnFirstFinished(this, null);
    }
}

In this setup the execution blocks (black loading screen) and the first controller isn't loaded.

If the call to PresentFirstController() is removed from ViewDidLoad(), the main controller loads fine and when clicking the "Test" button the first controller is loaded. However, after the first controller is dismissed, the second controller is NOT loaded - iOS doesn't seem to like presenting a modal controller right after dismissing another one. This can be solved by adding a small delay (but how small is still safe?) like below:

public override void DismissModalViewControllerAnimated (bool animated)
{
    base.DismissModalViewControllerAnimated (animated);

    NSRunLoop.Current.RunUntil (NSDate.FromTimeIntervalSinceNow(0.2));

    if(null != OnFirstFinished)
    {
        OnFirstFinished(this, null);
    }
}

Upvotes: 2

Views: 3365

Answers (1)

miguel.de.icaza
miguel.de.icaza

Reputation: 32694

In general, the code pattern that you want to use on iOS is to chain these actions. For example, your view controller would likely have a login/password field, and a button to do the login.

What you would do is connect an action to the Login button that contacts the server, validates the user and if the credentials are OK, dismiss the dialog view controller and at that point resume execution.

That said, you could present the view controller and run the UI main loop manually and wait for some event to trigger before resuming execution.

do {
    NSRunLoop.Current.RunUntil (NSDate.FromTimeIntervalSinceNow (0.5));
} while (!done);

You can save yourself some pain and structure your code with the UIKit patterns instead of trying to fight them. You will end up saving time.

Upvotes: 2

Related Questions