如何在没有 ViewController 的情况下初始化可重用的 NSView 内置 Interface Builder(由 Xib 支持)
How to Initialize a Reusable NSView Built in Interface Builder (Backed by a Xib) Without a ViewController
我正在尝试制作一个可重复使用的视图,我可以通过编程方式在超级视图中实例化多个副本。但是,我想在 Interface Builder 中处理这个可重用视图的约束和基本布局。对于我的超级视图,我有一个 NSViewController
子类、一个 NSView
子类和一个 xib 文件。在我的 ViewController 的 ViewDidLoad
中,我有这样的代码来实例化子视图:
public override void ViewDidLoad()
{
base.ViewDidLoad();
var exampleView = new NSView();
View.AddSubview(exampleView);
View.AddConstraints(CreateExampleViewConstraints(exampleView, View));
NSView lastView = null;
foreach(var item in ExampleCollection)
{
var reuseableView = ReusableView.Create(item);
reuseableView.TranslatesAutoresizingMaskIntoConstraints = false;
exampleView.AddSubview(reuseableView);
var xPosConstraint = NSLayoutConstraint.Create(reuseableView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, exampleView, NSLayoutAttribute.Left, 1, 0);
NSLayoutConstraint yPosConstraint = null;
if(lastView != null)
{
yPosConstraint = NSLayoutConstraint.Create(reuseableView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, lastView, NSLayoutAttribute.Bottom, 1, 0);
}
else
{
yPosConstraint = NSLayoutConstraint.Create(reuseableView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, exampleView, NSLayoutAttribute.Top, 1, 0);
}
var widthConstraint = NSLayoutConstraint.Create(reuseableView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, exampleView, NSLayoutAttribute.Width, 1, 0);
var heightConstraint = NSLayoutConstraint.Create(reuseableView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1, 142);
exampleView.AddConstraint(xPosConstraint);
exampleView.AddConstraint(yPosConstraint);
exampleView.AddConstraint(widthConstraint);
exampleView.AddConstraint(heightConstraint);
lastView = reuseableView;
}
}
if(lastView != null)
{
var bottomPinConstraint = NSLayoutConstraint.Create(exampleView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, lastView, NSLayoutAttribute.Bottom, 1, 0);
exampleView.AddConstraint(bottomPinConstraint);
}
}
约束对于我要解决的问题并不重要。 ReusableView
是 NSView
的子类,有 3 个 NSTextField
出口,名称为 TitleLabel
、DateLabel
和 LocationLabel
:
public partial class ReusableView : AppKit.NSView
{
string _title;
string _date;
string _location;
public ReusableView(IntPtr handle) : base(handle)
{
}
[Export("initWithCoder:")]
public ReusableView(NSCoder coder) : base(coder)
{
}
public static ReusableView Create(ExampleDependency dependency)
{
var model = dependency ?? throw new ArgumentNullException(nameof(dependency));
NSBundle.MainBundle.LoadNibNamed(nameof(ReusableView), null, out var array);
var view = NSArray.FromArray<NSObject>(array).OfType<ReusableView>().FirstOrDefault();
view.Initialize(model);
return view;
}
private void Initialize(ExampleDependency dependency)
{
_title = dependency.name;
_date = dependency.date;
_location = $"{dependency.eventCity}, {dependency.eventState}";
}
public void UpdateControls()
{
TitleLabel.StringValue = _title;
DateLabel.StringValue = _date;
LocationLabel.StringValue = _location;
}
}
我 运行 遇到的问题是:在显示之前 ViewController 的生命周期方法中,ReusableView 的 IBOutlets 从来没有挂钩 up/not null。如您所见,我有一个名为 UpdateControls()
的 public 方法来尝试根据 Create()
期间初始化的私有字段设置标签的初始值。 Create()
方法确实成功加载了 xib 和 return ReusableView
的一个实例;我什至可以在运行时看到视图和占位符值。但是,我曾尝试在超级视图的视图控制器的 AwakeFromNib()
和 ViewWillAppear()
方法期间调用 UpdateControls()
,但总是得到一个空引用异常,因为 none 的出口显然是尚未实例化。
我显然可以完全以编程方式创建 ReusableView 并在代码中为控件设置约束,但我正在尝试利用界面生成器。有办法吗?
如果不为我的可重用视图添加视图控制器,我就无法让它工作。在 Xamarin 中,我使用了 Mac > View with Controller
模板并在生成的 xib 文件中布置了我的所有约束。然后,在我更改的代码中:
var reuseableView = ReusableView.Create(item);
reuseableView.TranslatesAutoresizingMaskIntoConstraints = false;
exampleView.AddSubview(reuseableView);
至:
var reusableViewController = new ReuseableViewController(item);
var reuseableView = reusableViewController.View;
reuseableView.TranslatesAutoresizingMaskIntoConstraints = false;
exampleView.AddSubview(reuseableView);
访问 ReuseableViewController
上的强类型 View
属性 确保我的 xib 将被加载并连接插座。在 ReuseableViewController
代码中,我有以下内容:
// Call to load from the XIB/NIB file
public ReuseableViewController(ExampleDependency dependency) : base("ReuseableViewController", NSBundle.MainBundle)
{
_dependency = dependency;
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
if(_dependency != null)
{
TitleLabel.StringValue = _dependency.name;
DateLabel.StringValue = _dependency.date;
LocationLabel.StringValue = $"{_dependency.eventCity}, {_dependency.eventState}";
}
}
我对这个答案不是 100% 满意,因为我觉得代码重用不需要视图控制器,所以如果其他人有更好的答案,请继续回复。
我正在尝试制作一个可重复使用的视图,我可以通过编程方式在超级视图中实例化多个副本。但是,我想在 Interface Builder 中处理这个可重用视图的约束和基本布局。对于我的超级视图,我有一个 NSViewController
子类、一个 NSView
子类和一个 xib 文件。在我的 ViewController 的 ViewDidLoad
中,我有这样的代码来实例化子视图:
public override void ViewDidLoad()
{
base.ViewDidLoad();
var exampleView = new NSView();
View.AddSubview(exampleView);
View.AddConstraints(CreateExampleViewConstraints(exampleView, View));
NSView lastView = null;
foreach(var item in ExampleCollection)
{
var reuseableView = ReusableView.Create(item);
reuseableView.TranslatesAutoresizingMaskIntoConstraints = false;
exampleView.AddSubview(reuseableView);
var xPosConstraint = NSLayoutConstraint.Create(reuseableView, NSLayoutAttribute.Left, NSLayoutRelation.Equal, exampleView, NSLayoutAttribute.Left, 1, 0);
NSLayoutConstraint yPosConstraint = null;
if(lastView != null)
{
yPosConstraint = NSLayoutConstraint.Create(reuseableView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, lastView, NSLayoutAttribute.Bottom, 1, 0);
}
else
{
yPosConstraint = NSLayoutConstraint.Create(reuseableView, NSLayoutAttribute.Top, NSLayoutRelation.Equal, exampleView, NSLayoutAttribute.Top, 1, 0);
}
var widthConstraint = NSLayoutConstraint.Create(reuseableView, NSLayoutAttribute.Width, NSLayoutRelation.Equal, exampleView, NSLayoutAttribute.Width, 1, 0);
var heightConstraint = NSLayoutConstraint.Create(reuseableView, NSLayoutAttribute.Height, NSLayoutRelation.Equal, 1, 142);
exampleView.AddConstraint(xPosConstraint);
exampleView.AddConstraint(yPosConstraint);
exampleView.AddConstraint(widthConstraint);
exampleView.AddConstraint(heightConstraint);
lastView = reuseableView;
}
}
if(lastView != null)
{
var bottomPinConstraint = NSLayoutConstraint.Create(exampleView, NSLayoutAttribute.Bottom, NSLayoutRelation.Equal, lastView, NSLayoutAttribute.Bottom, 1, 0);
exampleView.AddConstraint(bottomPinConstraint);
}
}
约束对于我要解决的问题并不重要。 ReusableView
是 NSView
的子类,有 3 个 NSTextField
出口,名称为 TitleLabel
、DateLabel
和 LocationLabel
:
public partial class ReusableView : AppKit.NSView
{
string _title;
string _date;
string _location;
public ReusableView(IntPtr handle) : base(handle)
{
}
[Export("initWithCoder:")]
public ReusableView(NSCoder coder) : base(coder)
{
}
public static ReusableView Create(ExampleDependency dependency)
{
var model = dependency ?? throw new ArgumentNullException(nameof(dependency));
NSBundle.MainBundle.LoadNibNamed(nameof(ReusableView), null, out var array);
var view = NSArray.FromArray<NSObject>(array).OfType<ReusableView>().FirstOrDefault();
view.Initialize(model);
return view;
}
private void Initialize(ExampleDependency dependency)
{
_title = dependency.name;
_date = dependency.date;
_location = $"{dependency.eventCity}, {dependency.eventState}";
}
public void UpdateControls()
{
TitleLabel.StringValue = _title;
DateLabel.StringValue = _date;
LocationLabel.StringValue = _location;
}
}
我 运行 遇到的问题是:在显示之前 ViewController 的生命周期方法中,ReusableView 的 IBOutlets 从来没有挂钩 up/not null。如您所见,我有一个名为 UpdateControls()
的 public 方法来尝试根据 Create()
期间初始化的私有字段设置标签的初始值。 Create()
方法确实成功加载了 xib 和 return ReusableView
的一个实例;我什至可以在运行时看到视图和占位符值。但是,我曾尝试在超级视图的视图控制器的 AwakeFromNib()
和 ViewWillAppear()
方法期间调用 UpdateControls()
,但总是得到一个空引用异常,因为 none 的出口显然是尚未实例化。
我显然可以完全以编程方式创建 ReusableView 并在代码中为控件设置约束,但我正在尝试利用界面生成器。有办法吗?
如果不为我的可重用视图添加视图控制器,我就无法让它工作。在 Xamarin 中,我使用了 Mac > View with Controller
模板并在生成的 xib 文件中布置了我的所有约束。然后,在我更改的代码中:
var reuseableView = ReusableView.Create(item);
reuseableView.TranslatesAutoresizingMaskIntoConstraints = false;
exampleView.AddSubview(reuseableView);
至:
var reusableViewController = new ReuseableViewController(item);
var reuseableView = reusableViewController.View;
reuseableView.TranslatesAutoresizingMaskIntoConstraints = false;
exampleView.AddSubview(reuseableView);
访问 ReuseableViewController
上的强类型 View
属性 确保我的 xib 将被加载并连接插座。在 ReuseableViewController
代码中,我有以下内容:
// Call to load from the XIB/NIB file
public ReuseableViewController(ExampleDependency dependency) : base("ReuseableViewController", NSBundle.MainBundle)
{
_dependency = dependency;
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
if(_dependency != null)
{
TitleLabel.StringValue = _dependency.name;
DateLabel.StringValue = _dependency.date;
LocationLabel.StringValue = $"{_dependency.eventCity}, {_dependency.eventState}";
}
}
我对这个答案不是 100% 满意,因为我觉得代码重用不需要视图控制器,所以如果其他人有更好的答案,请继续回复。