2

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);
    }
}
Community
  • 1
  • 1
Sorin Comanescu
  • 4,829
  • 3
  • 29
  • 38

1 Answers1

2

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.

miguel.de.icaza
  • 32,654
  • 6
  • 58
  • 76
  • Thanks Miguel, but this one locks just as every other attempt I had at waiting for the first modal to be dismissed. The code sequence I used is: 1) create first controller and subscribe to a custom "OnFinished" event; 2) PresentModalViewController() 3) execute the UI loop while "OnFinished" not triggered; 4) create second controller and PresentModalViewController() for it. The problem is the first controller doesn't even get displayed, while the execution is locked in the loop. Without the loop the first modal is displayed but doesn't wait for the controller to be dismissed. – Sorin Comanescu Dec 27 '11 at 08:19
  • Actually Miguel's suggestion does work, it's blocking only when calling the PresentModal() from the ViewDidLoad() override. Will post some code snippets soon. – Sorin Comanescu Dec 27 '11 at 09:01