如何使用 MvvmCross 6 在 Xamarin.iOS 中的代码中正确创建根视图控制器?
How to properly create root view controller in code in Xamarin.iOS with MvvmCross 6?
我正在尝试在代码中编写 Xamarin.iOS UI,并且正在关注 this MS tutorial。不幸的是,我无法找到将我的 MvxTabBarViewController
(MainViewController
) 设置为根控制器的方法。使用 NullReferenceException 实例化控制器结果。
在 MvvmCross 中将控制器设置为根控制器的正确方法是什么?
我的AppDelegate如下:
[Register(nameof(AppDelegate))]
public class AppDelegate : MvxApplicationDelegate<Setup, App>
{
public override void FinishedLaunching(UIApplication application)
{
base.FinishedLaunching(application);
}
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var controller = new MainViewController();
Window.RootViewController = controller;
Window.MakeKeyAndVisible();
Debug.WriteLine("Set root view to MainViewController");
return true;
}
}
并且视图控制器是 root:
[MvxRootPresentation]
public partial class MainViewController : MvxTabBarViewController<MainViewModel>
{
private UILabel label;
//public MainViewController(IntPtr handle) : base(handle)
//{
//}
public override void ViewDidLoad()
{
base.ViewDidLoad();
Debug.WriteLine("Main View Controller loaded");
AddLabel();
}
private void AddLabel()
{
label = new UILabel
{
Text = "testing",
TranslatesAutoresizingMaskIntoConstraints = false
};
var lblConstraints = new[]
{
label.LeadingAnchor.ConstraintEqualTo(anchor:this.View.SafeAreaLayoutGuide.LeadingAnchor, constant:20.0f),
label.WidthAnchor.ConstraintEqualTo(label.IntrinsicContentSize.Width),
label.TopAnchor.ConstraintEqualTo(anchor:this.View.SafeAreaLayoutGuide.TopAnchor, constant:20.0f),
label.HeightAnchor.ConstraintEqualTo(label.IntrinsicContentSize.Height)
};
View.AddSubview(label);
}
}
异常堆栈跟踪:
at MvvmCross.Mvx.Resolve[TService] () [0x00006] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at MvvmCross.Platforms.Ios.Views.MvxBindingViewControllerAdapter..ctor (MvvmCross.Platforms.Ios.Views.Base.IMvxEventSourceViewController eventSource) [0x00034] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at MvvmCross.Platforms.Ios.Views.MvxViewControllerAdaptingExtensions.AdaptForBinding (MvvmCross.Platforms.Ios.Views.Base.IMvxEventSourceViewController view) [0x00007] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at MvvmCross.Platforms.Ios.Views.MvxBaseTabBarViewController..ctor () [0x00006] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at MvvmCross.Platforms.Ios.Views.MvxTabBarViewController..ctor () [0x00000] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at MvvmCross.Platforms.Ios.Views.MvxTabBarViewController`1[TViewModel]..ctor () [0x00000] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at PushNotifTest.iOS.Views.Main.MainViewController..ctor () <0x13d7f4970 + 0x0004a> in <1d11ff58113e46f6a5a9245eccb8c13f>:0
at PushNotifTest.iOS.AppDelegate.FinishedLaunching (UIKit.UIApplication application, Foundation.NSDictionary launchOptions) [0x00017] in /Users/dominik/Projekty/PushNotifTest/src/PushNotifTest.iOS/AppDelegate.cs:28
at (wrapper managed-to-native) UIKit.UIApplication.UIApplicationMain(int,string[],intptr,intptr)
at UIKit.UIApplication.Main (System.String[] args, System.IntPtr principal, System.IntPtr delegate) [0x00005] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.11.0.280/src/Xamarin.iOS/UIKit/UIApplication.cs:79
at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0002c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.11.0.280/src/Xamarin.iOS/UIKit/UIApplication.cs:63
at PushNotifTest.iOS.Application.Main (System.String[] args) [0x00001] in /Users/dominik/Projekty/PushNotifTest/src/PushNotifTest.iOS/Main.cs:9
你应该让 MvvmCross 为你做这件事。通常,您会指定在 IMvxAppStart
派生的 class.
中首先显示哪个 ViewModel
public class AppStart : IMvxAppStart
{
private readonly IMvxNavigationService _navigationService;
public AppStart(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
}
public void Start(object hint = null)
{
try
{
_navigationService.Navigate<MainViewModel>().GetAwaiter().GetResult();
}
catch (System.Exception e)
{
}
}
}
其中 MainViewModel
是与您的 ViewController 关联的 ViewModel,即 MvxTabBarViewController
。
然后只需在 AppDelegate 中执行 MvvmCross 希望您执行的常规仪式:
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var setup = new Setup(this, Window);
setup.Initialize();
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
Window.MakeKeyAndVisible();
return true;
}
因此 Setup
启动了 IoC 容器和其他相关的 MvvmCross 服务。然后你的 IMvxAppStart
导航到第一个 ViewController.
根据您如何归因于您的 MvxTabBarViewController
,它将被包装在导航中ViewController。您可以使用 MvxRootPresentationAttribute
.
控制它
[MvxRootPresentation(WrapInNavigationController = true)]
public partial class MainViewController : MvxTabBarViewController<MainViewModel>
编辑:
MvvmCross 6.0 让这部分变得更容易,而不是进行常规仪式。所以删除 AppDelegate 的主体并像这样创建它:
[Register("AppDelegate")]
public partial class AppDelegate : MvxApplicationDelegate<MvxIosSetup<App>, App>
{
}
在您的 Core 中,您有一个 App
class 实现了 MvxApplication
。这里在 Initialize()
覆盖中调用 RegisterAppStart<TViewModel>()
:
public class App : MvxApplication
{
public override void Initialize()
{
RegisterAppStart<RootViewModel>();
}
那就不要创建自己的 AppStart
class。你可以在App
class.
Startup()
中做UI绑定启动
我正在尝试在代码中编写 Xamarin.iOS UI,并且正在关注 this MS tutorial。不幸的是,我无法找到将我的 MvxTabBarViewController
(MainViewController
) 设置为根控制器的方法。使用 NullReferenceException 实例化控制器结果。
在 MvvmCross 中将控制器设置为根控制器的正确方法是什么?
我的AppDelegate如下:
[Register(nameof(AppDelegate))]
public class AppDelegate : MvxApplicationDelegate<Setup, App>
{
public override void FinishedLaunching(UIApplication application)
{
base.FinishedLaunching(application);
}
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var controller = new MainViewController();
Window.RootViewController = controller;
Window.MakeKeyAndVisible();
Debug.WriteLine("Set root view to MainViewController");
return true;
}
}
并且视图控制器是 root:
[MvxRootPresentation]
public partial class MainViewController : MvxTabBarViewController<MainViewModel>
{
private UILabel label;
//public MainViewController(IntPtr handle) : base(handle)
//{
//}
public override void ViewDidLoad()
{
base.ViewDidLoad();
Debug.WriteLine("Main View Controller loaded");
AddLabel();
}
private void AddLabel()
{
label = new UILabel
{
Text = "testing",
TranslatesAutoresizingMaskIntoConstraints = false
};
var lblConstraints = new[]
{
label.LeadingAnchor.ConstraintEqualTo(anchor:this.View.SafeAreaLayoutGuide.LeadingAnchor, constant:20.0f),
label.WidthAnchor.ConstraintEqualTo(label.IntrinsicContentSize.Width),
label.TopAnchor.ConstraintEqualTo(anchor:this.View.SafeAreaLayoutGuide.TopAnchor, constant:20.0f),
label.HeightAnchor.ConstraintEqualTo(label.IntrinsicContentSize.Height)
};
View.AddSubview(label);
}
}
异常堆栈跟踪:
at MvvmCross.Mvx.Resolve[TService] () [0x00006] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at MvvmCross.Platforms.Ios.Views.MvxBindingViewControllerAdapter..ctor (MvvmCross.Platforms.Ios.Views.Base.IMvxEventSourceViewController eventSource) [0x00034] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at MvvmCross.Platforms.Ios.Views.MvxViewControllerAdaptingExtensions.AdaptForBinding (MvvmCross.Platforms.Ios.Views.Base.IMvxEventSourceViewController view) [0x00007] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at MvvmCross.Platforms.Ios.Views.MvxBaseTabBarViewController..ctor () [0x00006] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at MvvmCross.Platforms.Ios.Views.MvxTabBarViewController..ctor () [0x00000] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at MvvmCross.Platforms.Ios.Views.MvxTabBarViewController`1[TViewModel]..ctor () [0x00000] in <8a077b300d9c484ab0471c2d21c3cb26>:0
at PushNotifTest.iOS.Views.Main.MainViewController..ctor () <0x13d7f4970 + 0x0004a> in <1d11ff58113e46f6a5a9245eccb8c13f>:0
at PushNotifTest.iOS.AppDelegate.FinishedLaunching (UIKit.UIApplication application, Foundation.NSDictionary launchOptions) [0x00017] in /Users/dominik/Projekty/PushNotifTest/src/PushNotifTest.iOS/AppDelegate.cs:28
at (wrapper managed-to-native) UIKit.UIApplication.UIApplicationMain(int,string[],intptr,intptr)
at UIKit.UIApplication.Main (System.String[] args, System.IntPtr principal, System.IntPtr delegate) [0x00005] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.11.0.280/src/Xamarin.iOS/UIKit/UIApplication.cs:79
at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0002c] in /Library/Frameworks/Xamarin.iOS.framework/Versions/11.11.0.280/src/Xamarin.iOS/UIKit/UIApplication.cs:63
at PushNotifTest.iOS.Application.Main (System.String[] args) [0x00001] in /Users/dominik/Projekty/PushNotifTest/src/PushNotifTest.iOS/Main.cs:9
你应该让 MvvmCross 为你做这件事。通常,您会指定在 IMvxAppStart
派生的 class.
public class AppStart : IMvxAppStart
{
private readonly IMvxNavigationService _navigationService;
public AppStart(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
}
public void Start(object hint = null)
{
try
{
_navigationService.Navigate<MainViewModel>().GetAwaiter().GetResult();
}
catch (System.Exception e)
{
}
}
}
其中 MainViewModel
是与您的 ViewController 关联的 ViewModel,即 MvxTabBarViewController
。
然后只需在 AppDelegate 中执行 MvvmCross 希望您执行的常规仪式:
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
var setup = new Setup(this, Window);
setup.Initialize();
var startup = Mvx.Resolve<IMvxAppStart>();
startup.Start();
Window.MakeKeyAndVisible();
return true;
}
因此 Setup
启动了 IoC 容器和其他相关的 MvvmCross 服务。然后你的 IMvxAppStart
导航到第一个 ViewController.
根据您如何归因于您的 MvxTabBarViewController
,它将被包装在导航中ViewController。您可以使用 MvxRootPresentationAttribute
.
[MvxRootPresentation(WrapInNavigationController = true)]
public partial class MainViewController : MvxTabBarViewController<MainViewModel>
编辑:
MvvmCross 6.0 让这部分变得更容易,而不是进行常规仪式。所以删除 AppDelegate 的主体并像这样创建它:
[Register("AppDelegate")]
public partial class AppDelegate : MvxApplicationDelegate<MvxIosSetup<App>, App>
{
}
在您的 Core 中,您有一个 App
class 实现了 MvxApplication
。这里在 Initialize()
覆盖中调用 RegisterAppStart<TViewModel>()
:
public class App : MvxApplication
{
public override void Initialize()
{
RegisterAppStart<RootViewModel>();
}
那就不要创建自己的 AppStart
class。你可以在App
class.
Startup()
中做UI绑定启动