Xamarin Designer for iOS 中的自定义控件
Xamarin Designer for iOS 支持呈现在项目中创建或引用自外部源(如 Xamarin 组件商店)的自定义控件。
警告
iOS Designer 在 Visual Studio 2019 版本 16.8 和 Visual Studio 2019 for Mac 版本 8.8 中已经弃用,并且已从 Visual Studio 2019 版本 16.9 和 Visual Studio for Mac 版本 8.9 中移除。 要生成 iOS 用户界面,建议直接在运行 Xcode 的 Mac 上操作。 有关详细信息,请参阅用 Xcode 设计用户界面。
Xamarin Designer for iOS 是一个功能强大的工具,用于可视化应用程序的用户界面,并为大多数 iOS 视图和视图控制器提供 WYSIWYG 编辑支持。 你的应用还可能包含扩展 iOS 中的内置控件的自定义控件。 如果在编写这些自定义控件时考虑到了一些准则,那么 iOS 设计器也可以呈现这些控件,从而提供更丰富的编辑体验。 本文档将介绍这些准则。
要求
满足以下所有要求的控件将在设计图面上呈现:
- 它是 UIView 或 UIViewController 的直接或间接子类。 其他 NSObject 子类将在设计图面上显示为图标。
- 它有一个 RegisterAttribute,以将其公开给 Objective-C。
- 它具有所需的 IntPtr 构造函数。
- 它实现了 IComponent 接口,或者将 DesignTimeVisibleAttribute 设置为 True。
当为模拟器编译代码中定义的控件的包含项目时,满足上述要求的控件将出现在设计器中。 默认情况下,所有自定义控件都将显示在“工具箱”的“自定义组件”部分中。 但是,CategoryAttribute 可以应用于自定义控件的类,以指定其他部分。
该设计器不支持加载第三方 Objective-C 库。
自定义属性
如果满足以下条件,自定义控件声明的属性将显示在属性面板中:
- 该属性具有公共 getter 和 setter。
- 该属性具有 ExportAttribute 以及设置为 True 的 BrowsableAttribute。
- 属性类型为数值类型、枚举类型、字符串、bool、SizeF、UIColor 或 UIImage。 此受支持类型的列表将来还有可能会扩大。
该属性还可以使用 DisplayNameAttribute 进行修饰,以指定在属性面板中为其显示的标签。
初始化
对于 UIViewController
子类,应对依赖于你在设计器中创建的视图的代码使用 ViewDidLoad 方法。
对于 UIView
和其他 NSObject
子类,在从布局文件加载自定义控件后,建议使用 AwakeFromNib 方法对其执行初始化。 这是因为不会在运行控件的构造函数时设置属性面板中设置的任何自定义属性,而是会在调用 AwakeFromNib
之前设置这些属性:
[Register ("CustomView"), DesignTimeVisible (true)]
public class CustomView : UIView {
public CustomView (IntPtr handle) : base (handle) { }
public override void AwakeFromNib ()
{
// Initialize the view here.
}
}
如果控件还设计为直接从代码创建,则可能需要创建一个具有通用初始化代码的方法,如下所示:
[Register ("CustomView"), DesignTimeVisible (true)]
public class CustomView : UIView {
public CustomView (IntPtr handle) : base (handle) { }
public CustomView ()
{
// Called when created from code.
Initialize ();
}
public override void AwakeFromNib ()
{
// Called when loaded from xib or storyboard.
Initialize ();
}
void Initialize ()
{
// Common initialization code here.
}
}
属性初始化和 AwakeFromNib
请注意将在何时、何处对自定义组件中的可设计属性进行初始化,以避免覆盖在 iOS 设计器中设置的值。 以以下代码为例:
[Register ("CustomView"), DesignTimeVisible (true)]
public class CustomView : UIView {
[Export ("Counter"), Browsable (true)]
public int Counter {get; set;}
public CustomView (IntPtr handle) : base (handle) { }
public CustomView ()
{
// Called when created from code.
Initialize ();
}
public override void AwakeFromNib ()
{
// Called when loaded from xib or storyboard.
Initialize ();
}
void Initialize ()
{
// Common initialization code here.
Counter = 0;
}
}
CustomView
组件公开了可由开发人员在 iOS 设计器内设置的 Counter
属性。 但是,无论在设计器中设置的值是什么,Counter
属性的值始终为零 (0)。 原因如下:
CustomControl
实例从情节提要文件中进行了膨胀。- 设置了在 iOS 设计器中修改的所有属性(例如,将
Counter
的值设置为 2)。 - 执行了
AwakeFromNib
方法,并调用了组件的Initialize
方法。 - 在
Initialize
内部,Counter
属性的值将重置为零 (0)。
若要解决上述情况,请在其他位置(如组件的构造函数中)初始化 Counter
属性,或者如果组件不需要在其构造函数当前处理的内容之外进行进一步初始化,则不要重写 AwakeFromNib
方法并调用 Initialize
。
设计模式
在设计图面上,自定义控件必须遵循一些限制:
- 应用程序包资源在设计模式下不可用。 图像将在通过 UIImage 方法加载后将变得可用。
- 不应在设计模式下执行异步操作(如 Web 请求)。 设计图面不支持动画或对控件 UI 的任何其他异步更新。
自定义控件可以实现 IComponent,并使用 DesignMode 属性检查它是否位于设计图面上。 在此示例中,标签将在设计图面上显示“Design Mode”,并在运行时显示“Runtime”:
[Register ("DesignerAwareLabel")]
public class DesignerAwareLabel : UILabel, IComponent {
#region IComponent implementation
public ISite Site { get; set; }
public event EventHandler Disposed;
#endregion
public DesignerAwareLabel (IntPtr handle) : base (handle) { }
public override void AwakeFromNib ()
{
if (Site != null && Site.DesignMode)
Text = "Design Mode";
else
Text = "Runtime";
}
}
在尝试访问其任何成员之前,必须检查 Site
属性是否为 null
。 如果 Site
为 null
,则假定控件不在设计器中运行是安全的。
在设计模式下,将在控件的构造函数运行之后以及调用 AwakeFromNib
之前设置 Site
。
调试
满足上述要求的控件将显示在工具箱中并呈现在图面上。 如果未呈现控件,请检查控件或其依赖项中是否存在 bug。
设计图面通常可以在继续呈现其他控件的同时,捕获单个控件引发的异常。 错误控件将替换为红色占位符,你可以通过单击该感叹号图标来查看异常跟踪:
如果控件显示有调试符号,则跟踪将具有文件名和行号。 双击堆栈跟踪中的某一行将跳转到源代码中的该行。
如果设计器无法隔离故障控件,则会在设计图面顶部显示警告消息:
在设计图面中修复或移除故障控件后,将恢复完整呈现。
总结
本文介绍了如何在 iOS 设计器中创建和应用自定义控件。 首先,介绍了控件必须满足哪些要求才能在设计图面上呈现所,并在属性面板中公开自定义属性。 然后,探讨了背后的代码 - 控件初始化和 DesignMode 属性。 最后,介绍了引发异常时发生的情况,以及如何解决此问题。