视图复用的自定义方式有两种
- code
- code + xib
code 方式
通过代码直接构造实例,或者在布局文件上通过 Custom Class 属性设置,在布局中设置需要为自定义类型添加 [Register ("")]
特性。
code + xib 方式
采用这种方式,需要我们手动关联 class 和 xib 视图,如
var lbl = new XLabel ();
UINib nib = UINib.FromName ("XLabel", NSBundle.MainBundle);
NSObject [] views = nib.Instantiate (lbl, null);
var niblbl = views [0] as UILabel;
niblbl.Frame = lbl.Bounds;
lbl.AddSubview (niblbl);
- 首先构造一个
XLabel
实例 - 构造对应的
nib
实例 - 将
nib
与lbl
关联,如设置outlets
- 获取
views
的顶层视图niblbl
并设置其Frame
- 添加
niblbl
到lbl
子视图中
通过上面 5 步,我们就可以复用该视图组件了。
为了避免对上面的代码重复的进行复制黏贴,通过我们会将其放置到 XLabel
的构造函数中,当实例化一个 XLabel
时,关联视图和类的过程也会随之完成。
[Register (nameof (XLabel))]
public class XLabel : UILabel
{
public XLabel (CGRect frame) : base (frame)
{
SetupSubviews ();
}
[Export ("initWithCoder:")]
public XLabel (NSCoder coder) : base (coder)
{
SetupSubviews ();
}
private void SetupSubviews ()
{
UINib nib = UINib.FromName ("XLabel", NSBundle.MainBundle);
NSObject [] views = nib.Instantiate (this, null);
var niblbl = views [0] as UILabel;
niblbl.Frame = this.Bounds;
this.AddSubview (niblbl);
}
}
当我们手动创建 XLabel
时会通过 CGRect
构造函数来创建,或者通过定义视图组件的 Custom Class
方式,最终通过调用 initWithCoder:
的方式来创建,两种方式最终都调用了 SetupSubviews()
方法完成视图和类的关联。
使用上面这种方式,需要注意的一点是,我们在定义 xib
时,要通过设置 File’s Owner 的 Custom Class,而不能直接设置 UILabel 的 Custom Class,因为 xib
的构造过程默认会调用视图上组件对应的 Custom Class 的 initWithCoder:
方法,而 initWithCoder:
方法中我们又定义了加载和构造当前 xib
文件,从而导致 initWithCoder:
方法被递归调用,直到栈溢出为止。所以,如果我们使用的视图的 Custom Class 属性,则不能够在 xib
中复用该视图组件,而需要使用代码的方式复用。Why you shouldn’t set top-level view’s custom class
除了通过上面获取nib
,然后实例化nib
两个步骤完成code
与xib
的关联,我们还可以使用NSBundle.MainBundle.LoadNib
的方式一次完成上述两个步骤的内容:
private void SetupSubviews ()
{
NSArray array = NSBundle.MainBundle.LoadNib ("XLabel", this, null);
XLabel niblbl = array.GetItem<XLabel> (0);
niblbl.Frame = this.Bounds;
this.AddSubview (niblbl);
}
除了 initWithCoder:
,Xamarin.iOS
还提供了另外一个构造函数,该构造函数包含了一个 IntPtr
类型的参数:
public XLabel (IntPtr handle) : base (handle)
{
}
关于 IntPtr
的解释如下
The IntPtr constructor is required for Objective-C to create instances of managed types used in storyboards/xibs, and if you’re missing the IntPtr constructor you’ll get Could not find an existing managed instance for this object, nor was it possible to create a new managed instance exception. 如果我们同时提供了
initWithCoder:
和IntPtr
的构造方式,默认initWithCoder:
会被调用,否则只会调用提供的那种方式。
关于使用 initWithCoder:
方式的兼容性建议