如何在故事板场景中嵌入自定义视图 xib?
How to embed a custom view xib in a storyboard scene?
我在 XCode/iOS 世界中相对较新;我已经完成了一些基于故事板的体面大小的应用程序,但我从来没有在整个 nib/xib 事情上切磋过。我想对场景使用相同的工具 design/layout 一个可重复使用的 view/control。所以我为我的视图 subclass 创建了我的第一个 xib 并将其绘制:
我连接了插座并设置了约束,就像我在故事板中习惯做的那样。我将 File Owner
的 class 设置为自定义 UIView
subclass 的 class。所以我假设我可以用一些 API 实例化这个视图 subclass,它会 configured/connected 如图所示。
现在回到我的故事板,我想 embed/reuse 这个。我在 table 视图原型单元格中这样做:
我有意见了。我已经将它的 class 设置为我的 subclass。我已经为它创建了一个出口,所以我可以操纵它。
64 美元的问题是 where/how 我是否指出仅将我的视图子class 的 empty/unconfigured 实例放在那里是不够的,而是使用我创建的 .xib要configure/instantiate吗?如果在 XCode6 中,那将非常酷,我可以只输入 XIB 文件以用于给定的 UIView,但我没有看到这样做的字段,所以我假设我必须在代码中做一些事情某处。
(我确实在 SO 上看到了其他类似的问题,但还没有发现任何只要求解决这部分难题的问题,或者是 XCode6/2015 的最新问题)
更新
我可以通过如下实现我的 table 单元格的 awakeFromNib
来完成这项工作:
- (void)awakeFromNib
{
// gather all of the constraints pointing to the uncofigured instance
NSArray* progressConstraints = [self.contentView.constraints filteredArrayUsingPredicate: [NSPredicate predicateWithBlock:^BOOL(id each, NSDictionary *_) {
return (((NSLayoutConstraint*)each).firstItem == self.progressControl) || (((NSLayoutConstraint*)each).secondItem == self.progressControl);
}]];
// fetch the fleshed out variant
ProgramProgressControl *fromXIB = [[[NSBundle mainBundle] loadNibNamed:@"ProgramProgressControl" owner:self options:nil] objectAtIndex:0];
// ape the current placeholder's frame
fromXIB.frame = self.progressControl.frame;
// now swap them
[UIView transitionFromView: self.progressControl toView: fromXIB duration: 0 options: 0 completion: nil];
// recreate all of the constraints, but for the new guy
for (NSLayoutConstraint *each in progressConstraints) {
id firstItem = each.firstItem == self.progressControl ? fromXIB : each.firstItem;
id secondItem = each.secondItem == self.progressControl ? fromXIB : each.secondItem;
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem: firstItem attribute: each.firstAttribute relatedBy: each.relation toItem: secondItem attribute: each.secondAttribute multiplier: each.multiplier constant: each.constant];
[self.contentView addConstraint: constraint];
}
// update our outlet
self.progressControl = fromXIB;
}
这有那么容易吗?还是我太努力了?
你快到了。您需要在将视图分配给的自定义 class 中覆盖 initWithCoder。
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:@"ViewYouCreated" owner:self options:nil] objectAtIndex:0]];
}
return self; }
一旦完成,StoryBoard 就会知道在该 UIView 中加载 xib。
这里有更详细的解释:
这是您的 UIViewController
在故事板上的样子:
蓝色 space 基本上是一个 UIView,它将 "hold" 你的 xib。
这是你的 xib:
有一个 Action 连接到它上面的一个按钮,可以打印一些文本。
这是最终结果:
第一个 clickMe 和第二个的区别在于,第一个是使用 StoryBoard
添加到 UIViewController
。第二个是使用代码添加的。
"correct" 答案是您不打算使用相应的 nib 制作可重复使用的视图。如果一个视图子类作为一个可重用的对象是有价值的,那么它很少需要一个 nib 来配合它。以 UIKit 提供的每个视图子类为例。这种想法的一部分是实际上有价值的视图子类不会使用 nib 实现,这是 Apple 的一般视图。
通常当您在 nib 或情节提要中使用视图时,无论如何您都希望针对给定的用例以图形方式对其进行调整。
您可以考虑使用 "copy paste" 来重新创建相同或相似的视图,而不是制作单独的笔尖。我相信可以满足相同的要求,它会让您或多或少地与 Apple 的做法保持一致。
您只需将 UIView
拖放到您的 IB 中并将其导出并设置
yourUIViewClass *yourView = [[[NSBundle mainBundle] loadNibNamed:@"yourUIViewClass" owner:self options:nil] firstObject];
[self.view addSubview:yourView]
步骤
- 添加新文件 => 用户界面 => UIView
- 设置自定义 Class - yourUIViewClass
- 设置恢复 ID - yourUIViewClass
yourUIViewClass *yourView = [[[NSBundle mainBundle] loadNibNamed:@"yourUIViewClass" owner:self options:nil] firstObject];
[self.view addSubview:yourView]
现在您可以根据需要自定义视图。
您需要在自定义 UIView
子类中实现 awakeAfterUsingCoder:
。此方法允许您将解码后的对象(来自故事板)与不同的对象(来自您的可重用 xib)交换,如下所示:
- (id) awakeAfterUsingCoder: (NSCoder *) aDecoder
{
// without this check you'll end up with a recursive loop - we need to know that we were loaded from our view xib vs the storyboard.
// set the view tag in the MyView xib to be -999 and anything else in the storyboard.
if ( self.tag == -999 )
{
return self;
}
// make sure your custom view is the first object in the nib
MyView* v = [[[UINib nibWithNibName: @"MyView" bundle: nil] instantiateWithOwner: nil options: nil] firstObject];
// copy properties forward from the storyboard-decoded object (self)
v.frame = self.frame;
v.autoresizingMask = self.autoresizingMask;
v.translatesAutoresizingMaskIntoConstraints = self.translatesAutoresizingMaskIntoConstraints;
v.tag = self.tag;
// copy any other attribtues you want to set in the storyboard
// possibly copy any child constraints for width/height
return v;
}
有一篇很好的文章 here 讨论了这项技术和一些替代方法。
此外,如果您将 IB_DESIGNABLE
添加到您的 @interface 声明,并提供一个 initWithFrame:
方法,您可以获得在 IB 中工作的设计时预览(需要 Xcode 6! ):
IB_DESIGNABLE @interface MyView : UIView
@end
@implementation MyView
- (id) initWithFrame: (CGRect) frame
{
self = [[[UINib nibWithNibName: @"MyView"
bundle: [NSBundle bundleForClass: [MyView class]]]
instantiateWithOwner: nil
options: nil] firstObject];
self.frame = frame;
return self;
}
一种非常酷且可重复使用的方式来执行此 Interface Builder 和 Swift 4:
像这样创建一个新的 class:
import Foundation
import UIKit
@IBDesignable class XibView: UIView {
@IBInspectable var xibName: String?
override func awakeFromNib() {
guard let name = self.xibName,
let xib = Bundle.main.loadNibNamed(name, owner: self),
let view = xib.first as? UIView else { return }
self.addSubview(view)
}
}
在您的情节提要中,添加一个 UIView 作为 Xib 的容器。给它起一个 class 的名字 XibView
:
在这个新 XibView
的 属性 检查器中,在 IBInspectable 字段中设置您的 .xib 的名称(不带文件扩展名):
向您的项目添加一个新的 Xib 视图,并在 属性 检查器中,将 Xib 的 "File's Owner" 设置为 XibView
(确保您只设置"File's Owner" 到您的自定义 class,不要子 class 内容视图,否则它会崩溃),然后再次设置 IBInspectable 字段:
需要注意的一件事: 这假定您正在将 .xib 框架与其容器相匹配。如果您不这样做,或者需要调整大小,则需要添加一些编程约束或修改子视图的框架以适应。我使用 snapkit 让事情变得简单:
xibView.snp_makeConstraints(closure: { (make) -> Void in
make.edges.equalTo(self)
})
奖励积分
据称您可以使用 prepareForInterfaceBuilder()
使这些可重用视图在 Interface Builder 中可见,但我运气不佳。 This blog建议加一个contentView
属性,调用如下:
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
xibSetup()
contentView?.prepareForInterfaceBuilder()
}
- 创建xib文件
File > New > New File > iOS > User Interface > View
- 创建自定义 UIView class
File > New > New File > iOS > Source > CocoaTouch
- 将 xib 文件的标识分配给自定义视图 class
- 在视图控制器的 viewDidLoad 中,在
NSBundle.mainBundle
上使用 loadNibNamed:
初始化 xib 及其关联文件,返回的第一个视图可以添加为 self.view 的子视图。
从nib加载的自定义视图可以保存到属性中,用于在viewDidLayoutSubviews
中设置边框。只需将框架设置为 self.view
的框架,除非您需要使其小于 self.view
。
class ViewController: UIViewController {
weak var customView: MyView!
override func viewDidLoad() {
super.viewDidLoad()
self.customView = NSBundle.mainBundle().loadNibNamed("MyView", owner: self, options: nil)[0] as! MyView
self.view.addSubview(customView)
addButtonHandlerForCustomView()
}
private func addButtonHandlerForCustomView() {
customView.buttonHandler = {
[weak self] (sender:UIButton) in
guard let welf = self else {
return
}
welf.buttonTapped(sender)
}
}
override func viewDidLayoutSubviews() {
self.customView.frame = self.view.frame
}
private func buttonTapped(button:UIButton) {
}
}
此外,如果您想从 xib 返回到您的 UIViewController
实例,请在自定义视图的 class 上创建一个弱 属性。
class MyView: UIView {
var buttonHandler:((sender:UIButton)->())!
@IBAction func buttonTapped(sender: UIButton) {
buttonHandler(sender:sender)
}
}
这是 GitHub
上的项目
@brandonscript 早期想法的快速版本 return:
override func awakeFromNib() {
guard let xibName = xibName,
let xib = Bundle.main.loadNibNamed(xibName, owner: self, options: nil),
let views = xib as? [UIView] else {
return
}
if views.count > 0 {
self.addSubview(views[0])
}
}
多年来我一直在使用这个代码片段。如果您计划在 XIB 中使用自定义 class 视图,只需将其放入自定义 class.
的 .m 文件中
作为副作用,它会导致调用 awakeFromNib,因此您可以将所有 init/setup 代码留在那里。
- (id)awakeAfterUsingCoder:(NSCoder*)aDecoder {
if ([[self subviews] count] == 0) {
UIView *view = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:nil options:nil][0];
view.frame = self.frame;
view.autoresizingMask = self.autoresizingMask;
view.alpha = self.alpha;
view.translatesAutoresizingMaskIntoConstraints = self.translatesAutoresizingMaskIntoConstraints;
return view;
}
return self;
}
虽然我不推荐您要走的路,但可以通过在您希望显示视图的位置放置 "embedded view controller view" 来完成。
嵌入包含单个视图的视图控制器 -- 您希望重复使用的视图。
这个问题已经有一段时间了,我看到了很多答案。我最近重新访问了它,因为我刚刚使用了 UIViewController 嵌入。哪个有效,直到你想在 "an element repeated at runtime" 中放置一些东西(例如 UICollectionViewCell 或 UITableViewCell)。 @TomSwift 提供的 link 让我遵循
的模式
A) 不要让父视图 class 成为自定义 class 类型,而是让 FileOwner 成为目标 class(在我的示例中,CycleControlsBar)
B) 嵌套小部件的任何 outlet/action linking 都指向
C) 在 CycleControlsBar 上实现这个简单的方法:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
if let container = (Bundle.main.loadNibNamed("CycleControlsBar", owner: self, options: nil) as? [UIView])?.first {
self.addSubview(container)
container.constrainToSuperview() // left as an excise for the student
}
}
非常有效,比其他方法简单得多。
这是您一直想要的答案。您可以只创建 CustomView
class,将它的主实例放在带有所有子视图和插座的 xib 中。然后,您可以将 class 应用到故事板或其他 xib 中的任何实例。
无需 fiddle 文件所有者,或将出口连接到代理或以特殊方式修改 xib,或添加自定义视图的实例作为其自身的子视图。
只需这样做:
- 导入 BFWControls 框架
- 将您的 superclass 从
UIView
更改为 NibView
(或从 UITableViewCell
更改为 NibTableViewCell
)
就是这样!
它甚至可以与 IBDesignable 一起在设计时在故事板中呈现您的自定义视图(包括来自 xib 的子视图)。
您可以在此处阅读更多相关信息:
https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
并且您可以在此处获取开源 BFWControls 框架:
https://github.com/BareFeetWare/BFWControls
下面是驱动它的代码的简单摘录,以防您感到好奇:
https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4
汤姆
我在 XCode/iOS 世界中相对较新;我已经完成了一些基于故事板的体面大小的应用程序,但我从来没有在整个 nib/xib 事情上切磋过。我想对场景使用相同的工具 design/layout 一个可重复使用的 view/control。所以我为我的视图 subclass 创建了我的第一个 xib 并将其绘制:
我连接了插座并设置了约束,就像我在故事板中习惯做的那样。我将 File Owner
的 class 设置为自定义 UIView
subclass 的 class。所以我假设我可以用一些 API 实例化这个视图 subclass,它会 configured/connected 如图所示。
现在回到我的故事板,我想 embed/reuse 这个。我在 table 视图原型单元格中这样做:
我有意见了。我已经将它的 class 设置为我的 subclass。我已经为它创建了一个出口,所以我可以操纵它。
64 美元的问题是 where/how 我是否指出仅将我的视图子class 的 empty/unconfigured 实例放在那里是不够的,而是使用我创建的 .xib要configure/instantiate吗?如果在 XCode6 中,那将非常酷,我可以只输入 XIB 文件以用于给定的 UIView,但我没有看到这样做的字段,所以我假设我必须在代码中做一些事情某处。
(我确实在 SO 上看到了其他类似的问题,但还没有发现任何只要求解决这部分难题的问题,或者是 XCode6/2015 的最新问题)
更新
我可以通过如下实现我的 table 单元格的 awakeFromNib
来完成这项工作:
- (void)awakeFromNib
{
// gather all of the constraints pointing to the uncofigured instance
NSArray* progressConstraints = [self.contentView.constraints filteredArrayUsingPredicate: [NSPredicate predicateWithBlock:^BOOL(id each, NSDictionary *_) {
return (((NSLayoutConstraint*)each).firstItem == self.progressControl) || (((NSLayoutConstraint*)each).secondItem == self.progressControl);
}]];
// fetch the fleshed out variant
ProgramProgressControl *fromXIB = [[[NSBundle mainBundle] loadNibNamed:@"ProgramProgressControl" owner:self options:nil] objectAtIndex:0];
// ape the current placeholder's frame
fromXIB.frame = self.progressControl.frame;
// now swap them
[UIView transitionFromView: self.progressControl toView: fromXIB duration: 0 options: 0 completion: nil];
// recreate all of the constraints, but for the new guy
for (NSLayoutConstraint *each in progressConstraints) {
id firstItem = each.firstItem == self.progressControl ? fromXIB : each.firstItem;
id secondItem = each.secondItem == self.progressControl ? fromXIB : each.secondItem;
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem: firstItem attribute: each.firstAttribute relatedBy: each.relation toItem: secondItem attribute: each.secondAttribute multiplier: each.multiplier constant: each.constant];
[self.contentView addConstraint: constraint];
}
// update our outlet
self.progressControl = fromXIB;
}
这有那么容易吗?还是我太努力了?
你快到了。您需要在将视图分配给的自定义 class 中覆盖 initWithCoder。
- (id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:@"ViewYouCreated" owner:self options:nil] objectAtIndex:0]];
}
return self; }
一旦完成,StoryBoard 就会知道在该 UIView 中加载 xib。
这里有更详细的解释:
这是您的 UIViewController
在故事板上的样子:
蓝色 space 基本上是一个 UIView,它将 "hold" 你的 xib。
这是你的 xib:
有一个 Action 连接到它上面的一个按钮,可以打印一些文本。
这是最终结果:
第一个 clickMe 和第二个的区别在于,第一个是使用 StoryBoard
添加到 UIViewController
。第二个是使用代码添加的。
"correct" 答案是您不打算使用相应的 nib 制作可重复使用的视图。如果一个视图子类作为一个可重用的对象是有价值的,那么它很少需要一个 nib 来配合它。以 UIKit 提供的每个视图子类为例。这种想法的一部分是实际上有价值的视图子类不会使用 nib 实现,这是 Apple 的一般视图。
通常当您在 nib 或情节提要中使用视图时,无论如何您都希望针对给定的用例以图形方式对其进行调整。
您可以考虑使用 "copy paste" 来重新创建相同或相似的视图,而不是制作单独的笔尖。我相信可以满足相同的要求,它会让您或多或少地与 Apple 的做法保持一致。
您只需将 UIView
拖放到您的 IB 中并将其导出并设置
yourUIViewClass *yourView = [[[NSBundle mainBundle] loadNibNamed:@"yourUIViewClass" owner:self options:nil] firstObject];
[self.view addSubview:yourView]
步骤
- 添加新文件 => 用户界面 => UIView
- 设置自定义 Class - yourUIViewClass
- 设置恢复 ID - yourUIViewClass
yourUIViewClass *yourView = [[[NSBundle mainBundle] loadNibNamed:@"yourUIViewClass" owner:self options:nil] firstObject]; [self.view addSubview:yourView]
现在您可以根据需要自定义视图。
您需要在自定义 UIView
子类中实现 awakeAfterUsingCoder:
。此方法允许您将解码后的对象(来自故事板)与不同的对象(来自您的可重用 xib)交换,如下所示:
- (id) awakeAfterUsingCoder: (NSCoder *) aDecoder
{
// without this check you'll end up with a recursive loop - we need to know that we were loaded from our view xib vs the storyboard.
// set the view tag in the MyView xib to be -999 and anything else in the storyboard.
if ( self.tag == -999 )
{
return self;
}
// make sure your custom view is the first object in the nib
MyView* v = [[[UINib nibWithNibName: @"MyView" bundle: nil] instantiateWithOwner: nil options: nil] firstObject];
// copy properties forward from the storyboard-decoded object (self)
v.frame = self.frame;
v.autoresizingMask = self.autoresizingMask;
v.translatesAutoresizingMaskIntoConstraints = self.translatesAutoresizingMaskIntoConstraints;
v.tag = self.tag;
// copy any other attribtues you want to set in the storyboard
// possibly copy any child constraints for width/height
return v;
}
有一篇很好的文章 here 讨论了这项技术和一些替代方法。
此外,如果您将 IB_DESIGNABLE
添加到您的 @interface 声明,并提供一个 initWithFrame:
方法,您可以获得在 IB 中工作的设计时预览(需要 Xcode 6! ):
IB_DESIGNABLE @interface MyView : UIView
@end
@implementation MyView
- (id) initWithFrame: (CGRect) frame
{
self = [[[UINib nibWithNibName: @"MyView"
bundle: [NSBundle bundleForClass: [MyView class]]]
instantiateWithOwner: nil
options: nil] firstObject];
self.frame = frame;
return self;
}
一种非常酷且可重复使用的方式来执行此 Interface Builder 和 Swift 4:
像这样创建一个新的 class:
import Foundation import UIKit @IBDesignable class XibView: UIView { @IBInspectable var xibName: String? override func awakeFromNib() { guard let name = self.xibName, let xib = Bundle.main.loadNibNamed(name, owner: self), let view = xib.first as? UIView else { return } self.addSubview(view) } }
在您的情节提要中,添加一个 UIView 作为 Xib 的容器。给它起一个 class 的名字
XibView
:在这个新
XibView
的 属性 检查器中,在 IBInspectable 字段中设置您的 .xib 的名称(不带文件扩展名):向您的项目添加一个新的 Xib 视图,并在 属性 检查器中,将 Xib 的 "File's Owner" 设置为
XibView
(确保您只设置"File's Owner" 到您的自定义 class,不要子 class 内容视图,否则它会崩溃),然后再次设置 IBInspectable 字段:
需要注意的一件事: 这假定您正在将 .xib 框架与其容器相匹配。如果您不这样做,或者需要调整大小,则需要添加一些编程约束或修改子视图的框架以适应。我使用 snapkit 让事情变得简单:
xibView.snp_makeConstraints(closure: { (make) -> Void in
make.edges.equalTo(self)
})
奖励积分
据称您可以使用 prepareForInterfaceBuilder()
使这些可重用视图在 Interface Builder 中可见,但我运气不佳。 This blog建议加一个contentView
属性,调用如下:
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
xibSetup()
contentView?.prepareForInterfaceBuilder()
}
- 创建xib文件
File > New > New File > iOS > User Interface > View
- 创建自定义 UIView class
File > New > New File > iOS > Source > CocoaTouch
- 将 xib 文件的标识分配给自定义视图 class
- 在视图控制器的 viewDidLoad 中,在
NSBundle.mainBundle
上使用loadNibNamed:
初始化 xib 及其关联文件,返回的第一个视图可以添加为 self.view 的子视图。 从nib加载的自定义视图可以保存到属性中,用于在
viewDidLayoutSubviews
中设置边框。只需将框架设置为self.view
的框架,除非您需要使其小于self.view
。class ViewController: UIViewController { weak var customView: MyView! override func viewDidLoad() { super.viewDidLoad() self.customView = NSBundle.mainBundle().loadNibNamed("MyView", owner: self, options: nil)[0] as! MyView self.view.addSubview(customView) addButtonHandlerForCustomView() } private func addButtonHandlerForCustomView() { customView.buttonHandler = { [weak self] (sender:UIButton) in guard let welf = self else { return } welf.buttonTapped(sender) } } override func viewDidLayoutSubviews() { self.customView.frame = self.view.frame } private func buttonTapped(button:UIButton) { } }
此外,如果您想从 xib 返回到您的
UIViewController
实例,请在自定义视图的 class 上创建一个弱 属性。class MyView: UIView { var buttonHandler:((sender:UIButton)->())! @IBAction func buttonTapped(sender: UIButton) { buttonHandler(sender:sender) } }
这是 GitHub
上的项目@brandonscript 早期想法的快速版本 return:
override func awakeFromNib() {
guard let xibName = xibName,
let xib = Bundle.main.loadNibNamed(xibName, owner: self, options: nil),
let views = xib as? [UIView] else {
return
}
if views.count > 0 {
self.addSubview(views[0])
}
}
多年来我一直在使用这个代码片段。如果您计划在 XIB 中使用自定义 class 视图,只需将其放入自定义 class.
的 .m 文件中作为副作用,它会导致调用 awakeFromNib,因此您可以将所有 init/setup 代码留在那里。
- (id)awakeAfterUsingCoder:(NSCoder*)aDecoder {
if ([[self subviews] count] == 0) {
UIView *view = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:nil options:nil][0];
view.frame = self.frame;
view.autoresizingMask = self.autoresizingMask;
view.alpha = self.alpha;
view.translatesAutoresizingMaskIntoConstraints = self.translatesAutoresizingMaskIntoConstraints;
return view;
}
return self;
}
虽然我不推荐您要走的路,但可以通过在您希望显示视图的位置放置 "embedded view controller view" 来完成。
嵌入包含单个视图的视图控制器 -- 您希望重复使用的视图。
这个问题已经有一段时间了,我看到了很多答案。我最近重新访问了它,因为我刚刚使用了 UIViewController 嵌入。哪个有效,直到你想在 "an element repeated at runtime" 中放置一些东西(例如 UICollectionViewCell 或 UITableViewCell)。 @TomSwift 提供的 link 让我遵循
的模式A) 不要让父视图 class 成为自定义 class 类型,而是让 FileOwner 成为目标 class(在我的示例中,CycleControlsBar)
B) 嵌套小部件的任何 outlet/action linking 都指向
C) 在 CycleControlsBar 上实现这个简单的方法:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
if let container = (Bundle.main.loadNibNamed("CycleControlsBar", owner: self, options: nil) as? [UIView])?.first {
self.addSubview(container)
container.constrainToSuperview() // left as an excise for the student
}
}
非常有效,比其他方法简单得多。
这是您一直想要的答案。您可以只创建 CustomView
class,将它的主实例放在带有所有子视图和插座的 xib 中。然后,您可以将 class 应用到故事板或其他 xib 中的任何实例。
无需 fiddle 文件所有者,或将出口连接到代理或以特殊方式修改 xib,或添加自定义视图的实例作为其自身的子视图。
只需这样做:
- 导入 BFWControls 框架
- 将您的 superclass 从
UIView
更改为NibView
(或从UITableViewCell
更改为NibTableViewCell
)
就是这样!
它甚至可以与 IBDesignable 一起在设计时在故事板中呈现您的自定义视图(包括来自 xib 的子视图)。
您可以在此处阅读更多相关信息: https://medium.com/build-an-app-like-lego/embed-a-xib-in-a-storyboard-953edf274155
并且您可以在此处获取开源 BFWControls 框架: https://github.com/BareFeetWare/BFWControls
下面是驱动它的代码的简单摘录,以防您感到好奇: https://gist.github.com/barefeettom/f48f6569100415e0ef1fd530ca39f5b4
汤姆