从 xib 文件加载视图时获取 System.InvalidCastException
Getting System.InvalidCastException while loading View from a xib file
我有一个 xib 文件 - LoaderView.xib 和关联的 class LoaderView.cs,其中包含用于显示加载文本的视图。当我尝试将它加载到主 ViewController 中时 -
public override void ViewDidAppear()
{
base.ViewDidAppear();
NSBundle.MainBundle.LoadNibNamed("LoaderView", this, out NSArray arr);
var view = Runtime.GetNSObject<LoaderView>(arr.ValueAt(0));
view.Frame = View.Frame;
View.AddSubview(view);
}
我在这一行中得到 System.InvalidCastException -
var view = Runtime.GetNSObject<LoaderView>(arr.ValueAt(0));
奇怪的是我每次都没有得到异常。当它工作时,我看到加载文本与默认视图重叠 - 与 ViewController.
相关联
有人可以指出什么是显示加载、错误和空状态的最佳方式。我应该为他们单独设置 ViewController 吗?或者我可以从主视图控制器中 load/unload 获得单独的 xib 文件吗?如何从 xib 文件加载视图?
问题出在arr。
在上面的图片中,可以看出方法 LoadNibNamed
并没有每次都以相同的顺序返回 arr
中的顶级对象。这就是为什么代码 -
var view = Runtime.GetNSObject<LoaderView>(arr.ValueAt(0));
无法每次都将 arr
的第 0 项转换为键入 LoaderView
。
解决方案是遍历数组以找到您的类型的项目 - LoaderView
此处。对于我的情况 - 我写了一个静态 class 它有一个通用方法 LoadViewFromNib
其中 returns 类型 T 的视图继承自 NSView
.
public static class LoadNib
{
static NSArray xibItems;
static nuint index;
public static T LoadViewFromNib<T>(string filename, NSObject owner) where T : NSView
{
NSBundle.MainBundle.LoadNibNamed(filename, owner, out xibItems);
for (nuint i = 0; i < xibItems.Count; i++)
{
NSObject item = xibItems.GetItem<NSObject>(i);
if (item is T)
{
index = i;
break;
}
}
return Runtime.GetNSObject<T>(xibItems.ValueAt(index));
}
}
关于重叠的问题,我发现子视图是透明的,除非它的背景被明确设置。
这是 Xamarin 的文档问题。他们的文档说使用第 0 个索引,但实际情况是数组顺序不确定。
Anagh 给出的答案是充分的,并且在技术上是最佳的,但您可以使用 Linq 使其更加简洁,并且它也非常适合扩展方法:
public static class NibLoadingExtension
{
public static T LoadViewFromNib<T>(this T @null) where T : NSView
{
NSBundle.MainBundle.LoadNibNamed(typeof(T).Name, null, out var array);
return NSArray.FromArray<NSObject>(array).OfType<T>().FirstOrDefault();
}
}
不幸的是,由于您不能在没有实例的情况下在 C# 中调用扩展方法,因此您必须像这样使用它:
var loaderView = default(LoaderView).LoadViewFromNib();
还有一个论据支持向每个单独的视图添加静态 Create() 方法或其他方法class,因为这样您就可以调用私有初始化程序,如下所示:
public static LoaderView Create(ExampleDependency dependency)
{
var model = dependency ?? throw new ArgumentNullException(nameof(dependency));
NSBundle.MainBundle.LoadNibNamed(nameof(LoaderView), null, out var array);
var view = NSArray.FromArray<NSObject>(array).OfType<LoaderView>().FirstOrDefault();
view.Initialize(model);
return view;
}
private void Initialize(ExampleDependency dependency)
{
_privateField1 = dependency.Value1;
_privateField2 = dependency.Value2;
_privateField3 = dependency.Value3;
}
我有一个 xib 文件 - LoaderView.xib 和关联的 class LoaderView.cs,其中包含用于显示加载文本的视图。当我尝试将它加载到主 ViewController 中时 -
public override void ViewDidAppear()
{
base.ViewDidAppear();
NSBundle.MainBundle.LoadNibNamed("LoaderView", this, out NSArray arr);
var view = Runtime.GetNSObject<LoaderView>(arr.ValueAt(0));
view.Frame = View.Frame;
View.AddSubview(view);
}
我在这一行中得到 System.InvalidCastException -
var view = Runtime.GetNSObject<LoaderView>(arr.ValueAt(0));
奇怪的是我每次都没有得到异常。当它工作时,我看到加载文本与默认视图重叠 - 与 ViewController.
相关联有人可以指出什么是显示加载、错误和空状态的最佳方式。我应该为他们单独设置 ViewController 吗?或者我可以从主视图控制器中 load/unload 获得单独的 xib 文件吗?如何从 xib 文件加载视图?
问题出在arr。
在上面的图片中,可以看出方法 LoadNibNamed
并没有每次都以相同的顺序返回 arr
中的顶级对象。这就是为什么代码 -
var view = Runtime.GetNSObject<LoaderView>(arr.ValueAt(0));
无法每次都将 arr
的第 0 项转换为键入 LoaderView
。
解决方案是遍历数组以找到您的类型的项目 - LoaderView
此处。对于我的情况 - 我写了一个静态 class 它有一个通用方法 LoadViewFromNib
其中 returns 类型 T 的视图继承自 NSView
.
public static class LoadNib
{
static NSArray xibItems;
static nuint index;
public static T LoadViewFromNib<T>(string filename, NSObject owner) where T : NSView
{
NSBundle.MainBundle.LoadNibNamed(filename, owner, out xibItems);
for (nuint i = 0; i < xibItems.Count; i++)
{
NSObject item = xibItems.GetItem<NSObject>(i);
if (item is T)
{
index = i;
break;
}
}
return Runtime.GetNSObject<T>(xibItems.ValueAt(index));
}
}
关于重叠的问题,我发现子视图是透明的,除非它的背景被明确设置。
这是 Xamarin 的文档问题。他们的文档说使用第 0 个索引,但实际情况是数组顺序不确定。
Anagh 给出的答案是充分的,并且在技术上是最佳的,但您可以使用 Linq 使其更加简洁,并且它也非常适合扩展方法:
public static class NibLoadingExtension
{
public static T LoadViewFromNib<T>(this T @null) where T : NSView
{
NSBundle.MainBundle.LoadNibNamed(typeof(T).Name, null, out var array);
return NSArray.FromArray<NSObject>(array).OfType<T>().FirstOrDefault();
}
}
不幸的是,由于您不能在没有实例的情况下在 C# 中调用扩展方法,因此您必须像这样使用它:
var loaderView = default(LoaderView).LoadViewFromNib();
还有一个论据支持向每个单独的视图添加静态 Create() 方法或其他方法class,因为这样您就可以调用私有初始化程序,如下所示:
public static LoaderView Create(ExampleDependency dependency)
{
var model = dependency ?? throw new ArgumentNullException(nameof(dependency));
NSBundle.MainBundle.LoadNibNamed(nameof(LoaderView), null, out var array);
var view = NSArray.FromArray<NSObject>(array).OfType<LoaderView>().FirstOrDefault();
view.Initialize(model);
return view;
}
private void Initialize(ExampleDependency dependency)
{
_privateField1 = dependency.Value1;
_privateField2 = dependency.Value2;
_privateField3 = dependency.Value3;
}