3

In our App we have a log-in ViewController A. On user log-in, a request navigate is automatically called to navigate to the next ViewController B. However when this is done we want to remove the log-in ViewController A from the stack so the user cannot "go back" to the log-in view but goes back the previous ViewController before the log-in instead.

We thought about removing the ViewController A from the stack when ViewController B is loaded, but is there a better way?

In the Android version of the App we've set history=no (if I recall correctly) and then it works.

Is there an similar way to achieve this in MonoTouch and MvvmCross?

Cheesebaron
  • 24,131
  • 15
  • 66
  • 118
Bjarke
  • 1,283
  • 11
  • 36

3 Answers3

8

I ended up with removing the unwanted viewcontroller from the navigation controller. In ViewDidDisappear() of my login ViewController I did the following:

public override void ViewDidDisappear (bool animated)
{
    if (this.NavigationController != null) {
        var controllers = this.NavigationController.ViewControllers;
        var newcontrollers = new UIViewController[controllers.Length - 1];
        int index = 0;
        foreach (var item in controllers) {
            if (item != this) {
                newcontrollers [index] = item;
                index++;
            }

        }
        this.NavigationController.ViewControllers = newcontrollers;
    }
    base.ViewDidDisappear(animated);
}

This way I way remove the unwanted ViewController when it is removed from the view. I am not fully convinced if it is the right way, but it is working rather good.

Cheesebaron
  • 24,131
  • 15
  • 66
  • 118
Bjarke
  • 1,283
  • 11
  • 36
1

This is quite a common scenario... so much so that we've included two mechanisms inside MvvmCross to allow this....

  1. a ClearTop parameter available in all ViewModel navigations.
  2. a RequestRemoveBackStep() call in all ViewModels - although this is currently NOT IMPLEMENTED IN iOS - sorry.
  3. If this isn't enough, then a third technique might be to use a custom presenter to help with your display logic.

To use : 1. a ClearTop parameter available in all ViewModel navigations.

To use this, simply include the ClearTop flag when navigating.

This is a boolean flag - so to use it just change:

this.RequestNavigate<ChildViewModel>(new {arg1 = val1});

to

this.RequestNavigate<ChildViewModel>(new {arg1 = val1}, true);

For a standard simple navigation controller presenter, this will end up calling ClearBackStack before your new view is shown:

    public override void ClearBackStack()
    {
        if (_masterNavigationController == null)
            return;

        _masterNavigationController.PopToRootViewController (true);
        _masterNavigationController = null;
    }

from https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Touch/Views/Presenters/MvxTouchViewPresenter.cs

If you are not using a standard navigation controller - e.g. if you had a tabbed, modal, popup or split view display then you will need to implement your own presentation logic to handle this.


You can't: 2. RequestRemoveBackStep().

Sadly it proved a bit awkward to implement this at a generic level for iOS - so currently that method is:

    public bool RequestRemoveBackStep()
    {
#warning What to do with ios back stack?
        // not supported on iOS really
        return false;
    }

from https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Touch/Views/MvxTouchViewDispatcher.cs

Sorry! I've raised a bug against this - https://github.com/slodge/MvvmCross/issues/80


3. You can always... Custom ideas

If you need to implement something custom for your iOS app, the best way is to do this through some sort of custom Presenter logic.

There are many ways you could do this.

One example is:

  • for any View or ViewModel which needs to clear the previous view, you could decorate the View or ViewModel with a [Special] attribute
  • in Show in your custom Presenter in your app, you could watch for that attribute and do the special behaviour at that time

    public override void Show(MvxShowViewModelRequest request)
    {
        if (request.ViewModelType.GetCustomAttributes(typeof(SpecialAttribute), true).Any())
        {
            // do custom behaviour here - e.g. pop current view controller
        }
    
        base.Show(request);
    }
    

Obviously other mechanisms might be available - it's just C# and UIKit code at this stage

Stuart
  • 66,722
  • 7
  • 114
  • 165
  • Hi Stuart, Thank you for the details. I believed it was a common scenario and hoped there was an easy way to do this. I will try your suggestion with the clearTop Boolean as it is a standard navigation Controller. I will do this first thing in the morning and then share my experience :) – Bjarke Dec 18 '12 at 21:12
  • Hi Stuart, what I missed in your explanation was the call poptorootviewcontroller. What I wanted was only to pop the previous viewcontroller, but you pointed me in the right direction. I ended up removing the unwanted viewcontroller from the navigationcontroller. It seems to work, however I am still not fully convinced it is the right way to go :) – Bjarke Dec 19 '12 at 12:18
  • Hi stuart, sorry for ripping this old thread, but do you have an example where the use of RequestRemoveBackStep() is applied in mono droid? I thought it was similar to RequestNavigate but I am not sure. – Bjarke Jan 21 '13 at 10:48
  • Apology accepted - but I think you need to ask new thought-out questions - SO frowns on discussions in comments. – Stuart Jan 21 '13 at 11:06
  • I just did, http://stackoverflow.com/questions/14437545/remove-activity-from-stack-using-mono-droid-and-slodge-mvvmcross – Bjarke Jan 21 '13 at 11:22
0

I don't know about mvvm but you can simply Pop the viewcontroller (AC A) without animation and then push the new viewcontoller (AC B) with animation

From within AC A:

NavigationController.PopViewControllerAnimated(false);
NavigationController.PushViewController(new ACb(), true);
svn
  • 1,235
  • 10
  • 22