Search code examples
xamarin.iosmvvmcross

Mvvmcross - Container UIViewController


I am trying to implement a Custom UIViewController class that will act as my navigation controller rather than using the default navigation controller. Below is my container class I have:

enter code herepublic partial class ContainerView : MvxViewController { static bool UserInterfaceIdiomIsPhone { get { return UIDevice.CurrentDevice.UserInterfaceIdiom == UIUserInterfaceIdiom.Phone; } }

    private UIViewController _currentView;

    protected ContainerViewModel ContainerModel
    { get { return base.ViewModel as ContainerViewModel; } }

    public ContainerView ()
        : base (UserInterfaceIdiomIsPhone ? "ContainerView_iPhone" : "ContainerView_iPad", null)
    {

    }



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

        var loginView = this.CreateViewControllerFor(ContainerModel.Login) as UIViewController;
        ShowViewController(loginView);
        // Perform any additional setup after loading the view, typically from a nib.
    }

    public ContainerView(UIViewController controller)
        : base(UserInterfaceIdiomIsPhone ? "ContainerView_iPhone" : "ContainerView_iPad", null)
    {
        ShowViewController(controller);
    }

    public void ShowViewController(UIViewController viewController)
    {
        if (_currentView !=null)
        {
            RemoveCurrentViewController();
        }

        AddChildViewController(viewController);
        viewController.View.Frame = GetViewFrame();
        containerView.AddSubview(viewController.View);
        _currentView = viewController;
        viewController.DidMoveToParentViewController(this);

    }

    public void RemoveCurrentViewController()
    {
        _currentView.WillMoveToParentViewController(null);
        _currentView.View.RemoveFromSuperview();
        _currentView.RemoveFromParentViewController();

    }

    public RectangleF GetViewFrame()
    {
        return containerView.Bounds;
    }
}

My first load is fine as the login view is loaded into the containerView which is an outlet from the nib. The problem is on subsequent request my outlet is null. I may be doing this wrong but I am attempting to set the viewcontroller in the Presenter Show method as per below:

public class MyPresenter: MvxTouchViewPresenter
    {
        private static ContainerView _cv;

        public BlackhawkPresenter(MvxApplicationDelegate appDelegate, UIWindow window)
            : base(appDelegate, window)
        {
            _cv = new ContainerView();
            window.RootViewController = _cv;
        }

        protected override UINavigationController CreateNavigationController(UIViewController viewController)
        {
            return new BlackhawkNavigationController(viewController);
        }

        public override void Show(Cirrious.MvvmCross.ViewModels.MvxViewModelRequest request)
        {
            var viewController = (UIViewController)Mvx.Resolve<IMvxTouchViewCreator>().CreateView(request);
            if(request.ViewModelType == typeof(ContainerViewModel))
                base.Show(request);
            else
                _cv.ShowViewController(viewController);

        }

    }

for the else statement in the show as the "ViewDidLoad" never fires my outlet is null. I am not sure of any othe rway to intercept the call for ShowViewModel to display it in container View in my container class.


Solution

  • Your code is a bit confusing (a bit incomplete) but my guess is that you have more than one ContainerView created

    • one created during:

          _cv = new ContainerView();
      
    • a second one created during

              base.Show(request);
      

    If this is the case, then only one of these is actually being shown as the RootViewController and _cv might be left as a dangling reference to a not-used view controller.

    To find a solution, try taking a look at MvxTouchViewPresenter - you can either replace this class in its entirety (if you don't want any of it's functionality) or you can find one of the virtual methods to intercept/override to capture the variables you need - source code is https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Touch/Views/Presenters/MvxTouchViewPresenter.cs

    e.g. maybe something like this would work:

        protected override void ShowFirstView(UIViewController viewController)
        {
            _cv = (ContainerView)viewController;
            base.ShowFirstView(viewController);
        }
    

    ... but I'm sure plenty of other solutions are available.