Has anyone gotten Xamarin-Sidebar, MVVMCross & Storyboards to work together?

2.2k Views Asked by At

I've tried all permutations I can find on the web and just can't seem to get this to work.

I have my iOS app UI defined within a storyboard and the MVVM framework is mostly MVVMCross with some sprinkling of ReactiveUI thrown in for flavor.

I have a RootViewController defined where I generate the SideBarController that is attached to the AppDelegate as so:

[Register ("AppDelegate")]
public partial class AppDelegate : MvxApplicationDelegate
{
    public override UIWindow Window { 
        get;
        set;
    }

    public AppDelegate ()
    {
    }

    public SidebarController SidebarController { get; set; }
    public override void FinishedLaunching (UIApplication application)
    {
        var presenter = new MvxModalSupportTouchViewPresenter (this, Window);
        var setup = new Setup (this, presenter);

        setup.Initialize ();

        var startUp = Mvx.Resolve<IMvxAppStart> ();
        startUp.Start ();
    }
}

And in the RootViewController I have:

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

        if (ViewModel == null)
            return;

        var appDelegate = UIApplication.SharedApplication.Delegate as AppDelegate;      
        appDelegate.SidebarController = new SidebarController (this, 
            (UIViewController)this.CreateViewControllerFor<LoginViewModel>(), 
            (UIViewController)this.CreateViewControllerFor<SideMenuViewModel>());
        appDelegate.SidebarController.MenuWidth = 250;
        appDelegate.SidebarController.ReopenOnRotate = false;
        appDelegate.SidebarController.MenuLocation = SidebarController.MenuLocations.Right;
    }

After the user has a successful login they are then navigated to a landing page that has a burger button on it. Once the user clicks the burger button the side menu isn't show but all tracing before and after the ToggleMenu() call gets executed. I'm racking my brain trying to get this to work but after 3 days I think I may have given myself a concussion.

Has anyone tried to get this combo working?

2

There are 2 best solutions below

1
On BEST ANSWER

So after looking over the code that Martijn posted and then actually doing some snooping through his previous posts, I decided that it would be best to create my own MvxViewPresenter and so far it works.

public class SideBarControllerTouchViewPresenter : MvxModalSupportTouchViewPresenter
{
    private UIWindow _window;

    public SidebarController SidebarController {
        get ;
        set ;
    }

    public UIViewController RootController {
        get;
        private set;
    }

    public SideBarControllerTouchViewPresenter (UIApplicationDelegate applicationDelegate, UIWindow window) 
        : base(applicationDelegate, window)
    {
        _window = window;
    }

    public override void ChangePresentation (Cirrious.MvvmCross.ViewModels.MvxPresentationHint hint)
    {
        base.ChangePresentation (hint);
    }

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

    protected override UINavigationController CreateNavigationController (UIViewController viewController)
    {

        var navController = new UINavigationController (viewController);
        var menuController = UIStoryboard.FromName ("storyboard", null).InstantiateViewController ("MenuTableViewController") as MenuTableViewController;
        RootController = new UIViewController ();
        SidebarController = new SidebarController (this.RootController, navController, menuController);
        return navController;
    }

    protected override void SetWindowRootViewController (UIViewController controller)
    {
        _window.AddSubview (RootController.View);
        _window.RootViewController = RootController;
    }
}

So thanks for getting me the rest of the way there.

1
On

For me it is working. I am not exactly sure where your problems is, but this is my code:

[Register ("AppDelegate")]
    public partial class AppDelegate : MvxApplicationDelegate
    {
        UIWindow window;

        public RootViewController RootViewController { get { return window.RootViewController as RootViewController; } }

        public override bool FinishedLaunching (UIApplication app, NSDictionary options)
        {
            window = new UIWindow(UIScreen.MainScreen.Bounds);

            var presenter = new Presenter(this, window);

            var setup = new Setup(this, presenter);
            setup.Initialize();

            var startup = Mvx.Resolve<IMvxAppStart>();
            startup.Start();

            window.MakeKeyAndVisible();

            return true;
        }
    }

And for the rootcontroller:

public class RootViewController: UIViewController, IMvxCanCreateTouchView
    {
        public SidebarController SidebarController { get; private set; }
        private UIViewController root;

        public RootViewController (UIViewController root)
        {
            this.root = root;
        }

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

            var menuContentView = this.CreateViewControllerFor<MenuViewModel> () as MenuViewController;
            SidebarController = new SidebarController (this, root, menuContentView) {
                MenuLocation = SidebarController.MenuLocations.Left,
                HasShadowing = false
            };
        }

        public void NavigateToView (UIViewController view)
        {
            SidebarController.ChangeContentView (new UINavigationController (view));
        }
    }

As a Base class for my other controllers i have this:

public interface IControllerWithCustomNavigation
    {
        void NavigateToView (UIViewController controller);
    }

public class BaseController<TViewModel>: MvxViewController where TViewModel: BaseViewModel
    {
        protected bool NavigationBarEnabled = true;

        public new TViewModel ViewModel {
            get { return (TViewModel)base.ViewModel; }
            set { base.ViewModel = value; }
        }

        public BaseController (string nib) : base (nib, null)
        {
        }

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

            if (NavigationBarEnabled) {
                NavigationController.NavigationBarHidden = false;

                updateNavigationBar ();

                if (isRootViewModel()) {
                    addNavigationBarItemForMenu ();
                }
            } else if (NavigationController != null) {
                NavigationController.NavigationBarHidden = true;
            }
        }

        private void updateNavigationBar()
        {
            var navigationBar = NavigationController.NavigationBar;

            var colorTint = UIColor.FromRGB (133, 231, 110);
            var colorWhite = UIColor.White;

            navigationBar.BarTintColor = colorWhite;
            navigationBar.TintColor = colorTint;
            navigationBar.SetTitleTextAttributes (new UITextAttributes {
                TextColor = UIColor.FromRGB (13, 19, 26)
            });
        }

        private bool isRootViewModel()
        {
            return NavigationController.ViewControllers.Length < 2; // Every view, even the RootView, is counted here.
        }

    private void addNavigationBarItemForMenu()
    {
        var sidebarButton = new UIBarButtonItem (
            UIImage.FromFile ("menu_icon.png"),
            UIBarButtonItemStyle.Plain,
            (object sender, EventArgs e) => {
                (UIApplication.SharedApplication.Delegate as AppDelegate).RootViewController.SidebarController.ToggleMenu();
            }
        );

        NavigationItem.SetLeftBarButtonItem (sidebarButton, true);
    }

    public virtual void NavigateToView (UIViewController controller)
    {
        (UIApplication.SharedApplication.Delegate as AppDelegate).RootViewController.NavigateToView (controller);
    }
}