如何使用接口生成器创建的nib文件加载UIView

我试着做一些复杂的事情,但应该是可能的。所以这里有一个对你们所有专家的挑战(这个论坛是由你们很多人组成的:))。

我正在创建一个问卷“组件”,我想在NavigationContoller(我的QuestionManagerViewController)上加载它。“组件”是一个“空的”UIViewController,它可以根据需要回答的问题加载不同的视图。

我的做法是:

  1. 创建Question1View对象作为UIView子类,定义一些IBOutlets. view对象。
  2. 创建(使用接口生成器)Question1View.xib (这可能是我的问题所在)。我将UIViewControllerUIView都设置为Question1View类。
  3. 我将outlet链接到视图的组件(使用IB)。
  4. 我重写了我的QuestionManagerViewControllerinitWithNib,看起来像这样:

    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
    if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) {
    // Custom initialization
    }
    return self;
    }
    

When I run the code, I'm getting this error:

2009-05-14 15:05:37.152 iMobiDines[17148:20b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIViewController _loadViewFromNibNamed:bundle:] loaded the "Question1View" nib but the view outlet was not set.'

I'm sure there is a way to load the view using the nib file, without needing to create a viewController class.

307505 次浏览

你不应该在Interface Builder中将视图控制器的类设置为UIView的子类。这肯定是你的部分问题。将其保留为UIViewController,它的某个子类,或其他自定义类。

至于只从xib加载一个视图,我假设你有某种视图控制器(即使它没有扩展UIViewController,这可能对你的需求太重要了)设置为接口生成器中的文件的所有者,如果你想用它来定义你的接口。我也做了一些调查来证实这一点。这是因为否则就没有办法访问UIView中的任何接口元素,也没有办法让你自己的方法在代码中被事件触发。

如果你使用UIViewController作为视图的文件所有者,你可以使用initWithNibName:bundle:来加载它并取回视图控制器对象。在IB中,确保将view出口设置为xib中接口的视图。如果你使用其他类型的对象作为文件的所有者,你需要使用NSBundleloadNibNamed:owner:options:方法来加载nib,将文件所有者的实例传递给该方法。它的所有属性都将根据您在IB中定义的outlet进行正确设置。

谢谢大家。 我确实找到了一种方法来做我想做的事
  1. 用你需要的__abc1创建你的UIView
  2. 在IB中创建xib,按照你的喜好设计它,并像这样链接它:文件的所有者是类UIViewController(没有自定义子类,但“真正的”一个)。File Owner的视图连接到主视图,它的类被声明为步骤1中的类)。
  3. 将控件与__abc连接起来。
  4. DynamicViewController可以运行它的逻辑来决定加载什么视图/xib。一旦它做出了决定,在loadView方法中放入如下内容:

    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
    owner:self
    options:nil];
    
    
    QPickOneView* myView = [ nibViews objectAtIndex: 1];
    
    
    myView.question = question;
    

That's it!

The main bundle's loadNibNamed method will take care of initializing the view and creating the connections.

Now the ViewController can display a view or another depending on the data in memory, and the "parent" screen doesn't need to bother with this logic.

前面的答案没有考虑iPhone SDK 2.0到2.1之间发生的NIB (XIB)结构的变化。用户内容现在从索引0开始,而不是1。

你可以使用2.1宏,它适用于所有2.1及以上版本(在IPHONE前面加两个下划线):

 // Cited from previous example
NSArray* nibViews =  [[NSBundle mainBundle] loadNibNamed:@"QPickOneView" owner:self options:nil];
int startIndex;
#ifdef __IPHONE_2_1
startIndex = 0;
#else
startIndex = 1;
#endif
QPickOneView* myView = [ nibViews objectAtIndex: startIndex];
myView.question = question;

我们在大多数应用程序中都使用类似的技术。

巴尼

我发现Aaron Hillegass(作者,讲师,可可忍者)的这篇博文非常有启发性。即使您不采用他修改后的方法通过指定的初始化器加载NIB文件,您也至少可以更好地理解正在进行的过程。我最近一直在用这个方法,而且非常成功!

我也想做一些类似的事情,这是我发现的: (SDK 3.1.3) < / p >

我有一个视图控制器a(本身由一个导航控制器拥有),它在一个按钮按下加载VC B:

在AViewController.m

BViewController *bController = [[BViewController alloc] initWithNibName:@"Bnib" bundle:nil];
[self.navigationController pushViewController:bController animated:YES];
[bController release];

现在VC B有它的接口从Bnib,但当一个按钮被按下,我想去一个“编辑模式”有一个独立的UI从不同的nib,但我不想要一个新的VC的编辑模式,我想新的nib与我现有的B VC相关联。

在BViewController中。M(按按钮方式)

NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:@"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];

然后按下另一个按钮(退出编辑模式):

[editView removeFromSuperview];

我又变回了原来的Bnib

这很好,但请注意我的EditMode。nib中只有一个顶级obj,一个UIView obj。 不管这个nib中的文件所有者是设置为BViewController还是默认的NSObject,但要确保文件所有者中的视图出口没有设置为任何东西。 如果是,那么我得到一个exc_bad_access崩溃,xcode继续加载6677个堆栈帧 显示一个内部UIView方法被反复调用…看起来像一个无限循环。 (视图出口是设置在我原来的Bnib然而)

希望这能有所帮助。

我不确定一些答案在谈论什么,但我需要把这个答案放在这里,当我下次在谷歌中搜索时。关键词:“如何从一个nib加载一个UIView”或“如何从一个NSBundle加载一个UIView”

下面的代码几乎100%直接来自Apress iPhone 3开始书(第247页,“使用新的表视图单元格”):

- (void)viewDidLoad {
[super viewDidLoad];
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:@"Blah"
owner:self options:nil];
Blah *blah;
for (id object in bundle) {
if ([object isKindOfClass:[Blah class]]) {
blah = (Blah *)object;
break;
}
}
assert(blah != nil && "blah can't be nil");
[self.view addSubview: blah];
}

这假设你有一个名为BlahUIView子类,一个名为Blah的nib,其中包含一个将其类设置为BlahUIView


类别:NSObject + LoadFromNib

#import "NSObject+LoadFromNib.h"


@implementation NSObject (LoadFromNib)


+ (id)loadFromNib:(NSString *)name classToLoad:(Class)classToLoad {
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:name owner:self options:nil];
for (id object in bundle) {
if ([object isKindOfClass:classToLoad]) {
return object;
}
}
return nil;
}


@end

迅速扩展

extension UIView {
class func loadFromNib<T>(withName nibName: String) -> T? {
let nib  = UINib.init(nibName: nibName, bundle: nil)
let nibObjects = nib.instantiate(withOwner: nil, options: nil)
for object in nibObjects {
if let result = object as? T {
return result
}
}
return nil
}
}

还有一个正在使用的例子:

class SomeView: UIView {
class func loadFromNib() -> SomeView? {
return self.loadFromNib(withName: "SomeView")
}
}

还有一种更简单的方法来访问视图,而不是将nib作为数组处理。

1)创建一个自定义的View子类,其中包含你以后想要访问的任何出口。——MyView

2)在你想要加载和处理nib的UIViewController中,创建一个IBOutlet属性来保存加载的nib的视图,例如

在MyViewController中(一个UIViewController子类)

  @property (nonatomic, retain) IBOutlet UIView *myViewFromNib;

(别忘了合成它并在你的。m文件中发布它)

3)在IB中打开你的nib(我们将其称为'myViewNib.xib'),将你文件的所有者设置为MyViewController

4)现在连接你的文件的所有者出口myViewFromNib到nib的主视图。

现在在MyViewController中,写下面这行代码:

[[NSBundle mainBundle] loadNibNamed:@"myViewNib" owner:self options:nil];

当你这样做的时候,把你的财产称为“自我”。myViewFromNib”将让你从你的nib访问视图!

@AVeryDev

将加载的视图附加到视图控制器的视图:

[self.view addSubview:myViewFromNib];

据推测,有必要将其从视图中删除以避免内存泄漏。

澄清一下:视图控制器有几个iboutlet,其中一些连接到原始nib文件中的项(像往常一样),一些连接到加载的nib中的项。两个nib都有相同的所有者类。加载的视图覆盖了原始视图。

提示:在加载的笔尖中设置主视图的不透明度为零,那么它将不会从原始笔尖中模糊项目。

没有一个答案解释如何创建独立的XIB,这是这个问题的根源。“创建新的XIB文件”没有Xcode 4选项。

要做到这一点

1) Choose "New File..."
2) Choose the "User Interface" category under the iOS section
3) Choose the "View" item
4) You will then be prompted to choose an iPhone or iPad format

这看起来很简单,但它可以为您节省几分钟的时间,因为“XIB”这个词没有出现在任何地方。

这是一个很好的问题(+1),答案是几乎有帮助;)抱歉,伙计们,我花了很长时间才读完这篇文章,尽管Gonso &AVeryDev给出了很好的提示。希望这个答案能帮助到其他人。

MyVC是持有所有这些东西的视图控制器。

MySubview是我们想从xib中加载的视图

  • 在MyVC。xib,创建一个类型为MySubView的视图,大小合适;形状,放置在你想要的位置。
  • 在MyVC.h,有

    IBOutlet MySubview *mySubView
    // ...
    @property (nonatomic, retain) MySubview *mySubview;
    
  • In MyVC.m, @synthesize mySubView; and don't forget to release it in dealloc.

  • In MySubview.h, have an outlet/property for UIView *view (may be unnecessary, but worked for me.) Synthesize & release it in .m
  • In MySubview.xib
    • set file owner type to MySubview, and link the view property to your view.
    • Lay out all the bits & connect to the IBOutlet's as desired
  • Back in MyVC.m, have

    NSArray *xibviews = [[NSBundle mainBundle] loadNibNamed: @"MySubview" owner: mySubview options: NULL];
    MySubview *msView = [xibviews objectAtIndex: 0];
    msView.frame = mySubview.frame;
    UIView *oldView = mySubview;
    // Too simple: [self.view insertSubview: msView aboveSubview: mySubview];
    [[mySubview superview] insertSubview: msView aboveSubview: mySubview]; // allows nesting
    self.mySubview = msView;
    [oldCBView removeFromSuperview];
    

The tricky bit for me was: the hints in the other answers loaded my view from the xib, but did NOT replace the view in MyVC (duh!) -- I had to swap that out on my own.

Also, to get access to mySubview's methods, the view property in the .xib file must be set to MySubview. Otherwise, it comes back as a plain-old UIView.

If there's a way to load mySubview directly from its own xib, that'd rock, but this got me where I needed to be.

这是一件应该比较容易的事情。我最终扩展了UIViewController并添加了一个loadNib:inPlaceholder:选择器。现在我可以说

self.mySubview = (MyView *)[self loadNib:@"MyView" inPlaceholder:mySubview];

下面是这个类别的代码(它和Gonso描述的一样):

@interface UIViewController (nibSubviews)


- (UIView *)viewFromNib:(NSString *)nibName;
- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder;


@end


@implementation UIViewController (nibSubviews)


- (UIView *)viewFromNib:(NSString *)nibName
{
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
for (id view in xib) { // have to iterate; index varies
if ([view isKindOfClass:[UIView class]]) return view;
}
return nil;
}


- (UIView *)loadNib:(NSString *)nibName inPlaceholder:(UIView *)placeholder
{
UIView *nibView = [self viewFromNib:nibName];
[nibView setFrame:placeholder.frame];
[self.view insertSubview:nibView aboveSubview:placeholder];
[placeholder removeFromSuperview];
return nibView;
}


@end

你也可以使用UIViewController的initWithNibName而不是loadnibname。我发现这样更简单。

UIViewController *aViewController = [[UIViewController alloc] initWithNibName:@"MySubView" bundle:nil];
[self.subview addSubview:aViewController.view];
[aViewController release];  // release the VC

现在你只需要创建MySubView。xib和MySubView.h/m。在MySubView。xib设置文件的Owner类为UIViewController,视图类为MySubView。

你可以使用父文件xib来定位和调整subview的大小。

我有理由做同样的事情(从XIB文件编程加载视图),但我需要完全从UIView的子类的子类的上下文来做这件事(即不以任何方式涉及视图控制器)。为了做到这一点,我创建了这个实用方法:

+ (id) initWithNibName:(NSString *)nibName withSelf:(id)myself {


NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName
owner:myself options:nil];
for (id object in bundle) {
if ([object isKindOfClass:[myself class]]) {
return object;
}
}


return nil;
}

然后从我的子类的initWithFrame方法调用它,如下所示:

- (id)initWithFrame:(CGRect)frame {


self = [Utilities initWithNibName:@"XIB1" withSelf:self];
if (self) {
// Initialization code.
}
return self;
}

张贴的一般利益;如果有人发现没有这样做的问题,请告诉我。

对于所有那些需要管理多个自定义视图实例的人,即出口集合,我合并并定制了@Gonso, @AVeryDev和@Olie回答:

  1. 创建一个自定义MyView : UIView,并将其设置为所需XIB中根UIView的"自定义类"; 自定义类

  2. MyView中创建所有你需要的网点(现在就做,因为在第3点之后,IB会建议你连接UIViewController的出口,而不是我们想要的自定义视图); 自定义类outlet

    将你的UIViewController设置为自定义视图的"文件的所有者"; enter image description here

    UIViewController中,为你想要的每个MyView实例添加一个新的UIViews,并将它们连接到UIViewController,创建一个出口集合:这些视图将作为自定义视图实例的“包装器”视图; enter image description here

  3. 最后,在你的UIViewControllerviewDidLoad中添加以下行:

NSArray *bundleObjects;
MyView *currView;
NSMutableArray *myViews = [NSMutableArray arrayWithCapacity:myWrapperViews.count];
for (UIView *currWrapperView in myWrapperViews) {
bundleObjects = [[NSBundle mainBundle] loadNibNamed:@"MyView" owner:self options:nil];
for (id object in bundleObjects) {
if ([object isKindOfClass:[MyView class]]){
currView = (MyView *)object;
break;
}
}


[currView.myLabel setText:@"myText"];
[currView.myButton setTitle:@"myTitle" forState:UIControlStateNormal];
//...


[currWrapperView addSubview:currView];
[myViews addObject:currView];
}
//self.myViews = myViews; if need to access them later..

我最终为这个添加了一个类别到UIView:

 #import "UIViewNibLoading.h"


@implementation UIView (UIViewNibLoading)


+ (id) loadNibNamed:(NSString *) nibName {
return [UIView loadNibNamed:nibName fromBundle:[NSBundle mainBundle] retainingObjectWithTag:1];
}


+ (id) loadNibNamed:(NSString *) nibName fromBundle:(NSBundle *) bundle retainingObjectWithTag:(NSUInteger) tag {
NSArray * nib = [bundle loadNibNamed:nibName owner:nil options:nil];
if(!nib) return nil;
UIView * target = nil;
for(UIView * view in nib) {
if(view.tag == tag) {
target = [view retain];
break;
}
}
if(target && [target respondsToSelector:@selector(viewDidLoad)]) {
[target performSelector:@selector(viewDidLoad)];
}
return [target autorelease];
}


@end

解释:在ios&mac中Viewcontroller的视图加载更少

我有一种命名xib的约定,其中的视图与视图相同。和视图控制器一样。这样,我就不必在代码中写出类名了。我从同名的nib文件中加载一个UIView。

一个名为MyView的类的例子。

  • 创建一个名为MyView的nib文件。接口生成器中的xib
  • 在Interface Builder中添加一个UIView。将其类设置为MyView。自定义到你的内心的内容,连接MyView的实例变量到子视图,你可能想以后访问。
  • 在你的代码中,像这样创建一个新的MyView:

    . MyView * MyView = [MyView nib_viewfromnibwiowner:owner]

这是它的分类:

@implementation UIView (nib)


+ (id) nib_viewFromNib {
return [self nib_viewFromNibWithOwner:nil];
}


+ (id) nib_viewFromNibWithOwner:(id)owner {
NSString *className = NSStringFromClass([self class]);
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:className owner:owner options:nil];
UIView *view = nil;
for(UIView *v in nib) {
if ([v isKindOfClass:[self class]]) {
view = v;
break;
}
}
assert(view != nil && "View for class not found in nib file");
[view nib_viewDidLoad];
return view;
}


// override this to do custom setup
-(void)nib_viewDidLoad {


}

然后,我将连接按钮与我正在使用的控制器的动作,并使用自定义视图子类中的outlet在标签上设置内容。

我将使用UINib实例化一个自定义的UIView被重用

UINib *customNib = [UINib nibWithNibName:@"MyCustomView" bundle:nil];
MyCustomViewClass *customView = [[customNib instantiateWithOwner:self options:nil] objectAtIndex:0];
[self.view addSubview:customView];

这种情况下需要的文件是MyCustomView.xibMyCustomViewClass.hMyCustomViewClass.m 注意[UINib instantiateWithOwner]返回一个数组,所以你应该使用反映你想要重用的UIView的元素。在本例中,它是第一个元素

我做了一个我喜欢的分类:

UIView+NibInitializer.h

#import <UIKit/UIKit.h>


@interface UIView (NibInitializer)
- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil;
@end

UIView+NibInitializer.m

#import "UIView+NibInitializer.h"


@implementation UIView (NibInitializer)


- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
if (!nibNameOrNil) {
nibNameOrNil = NSStringFromClass([self class]);
}
NSArray *viewsInNib = [[NSBundle mainBundle] loadNibNamed:nibNameOrNil
owner:self
options:nil];
for (id view in viewsInNib) {
if ([view isKindOfClass:[self class]]) {
self = view;
break;
}
}
return self;
}


@end

然后,像这样调用:

MyCustomView *myCustomView = [[MyCustomView alloc] initWithNibNamed:nil];

如果你的nib不是你的类名,请使用一个nib名。

为了在你的子类中覆盖它以获得额外的行为,它可以看起来像这样:

- (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
{
self = [super initWithNibNamed:nibNameOrNil];
if (self) {
self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
}
return self;
}

下面是一种在Swift中实现的方法(目前正在XCode 7 beta 5中编写Swift 2.0)。

从你在接口生成器中设置为“自定义类”的UIView子类中创建一个这样的方法(我的子类名为RecordingFooterView):

class func loadFromNib() -> RecordingFooterView? {
let nib = UINib(nibName: "RecordingFooterView", bundle: nil)
let nibObjects = nib.instantiateWithOwner(nil, options: nil)
if nibObjects.count > 0 {
let topObject = nibObjects[0]
return topObject as? RecordingFooterView
}
return nil
}

然后你可以像这样调用它:

let recordingFooterView = RecordingFooterView.loadFromNib()

在斯威夫特

实际上我对这个问题的解决方案是,在我的CustonViewController的viewDidLoad中加载视图,我想这样使用视图:

myAccessoryView = NSBundle.mainBundle().loadNibNamed("MyAccessoryView", owner: self, options: nil)[0] as! MyAccessoryView

不要在loadView()方法中加载视图!loadView方法用于为你的自定义ViewController加载视图。

花了好几个小时后,我想出了以下解决办法。 遵循以下步骤创建自定义UIView

1)从UIView创建类myCustomView enter image description here < / p >

2)创建名称为myCustomView.xib enter image description here < / p > 3)在你的.xib文件中改变UIView的Class,在那里分配myCustomView Class enter image description here < / p > 4)创建iboutlet
enter image description here < / p > 5)在myCustomView * customView中加载.xib。 使用下面的示例代码。< / p >

myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
[myView.description setText:@"description"];
注:对于那些仍然面临问题的人可以评论,我将提供他们的链接 myCustomView

的示例项目

Swift用户可设计选项:

  1. 创建一个自定义的UIView子类和一个xib文件,我们将用自己的类名命名它:在我们的例子中是MemeView。在Meme View类内部,记得在类声明之前用@IBDesignable属性将其定义为可设计的
  2. 记住在身份检查器面板中使用自定义UIView子类在xib中设置文件的所有者

    enter image description here < / p >

  3. 在xib文件中,现在我们可以构建我们的界面,制作约束,创建出口,动作等。 李enter image description here < / p > < / >
  4. 我们需要实现一些自定义类的方法来在初始化后打开xib

    class XibbedView: UIView {

    weak var nibView: UIView!
    
    
    override convenience init(frame: CGRect) {
    let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
    self.init(nibName: nibName)
    }
    
    
    required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
    let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
    let nib = loadNib(nibName)
    nib.frame = bounds
    nib.translatesAutoresizingMaskIntoConstraints = false
    addSubview(nib)
    nibView = nib
    setUpConstraints()
    }
    
    
    init(nibName: String) {
    super.init(frame: CGRectZero)
    let nibName = NSStringFromClass(self.dynamicType).componentsSeparatedByString(".").last!
    let nib = loadNib(nibName)
    nib.frame = bounds
    nib.translatesAutoresizingMaskIntoConstraints = false
    addSubview(nib)
    nibView = nib
    setUpConstraints()
    }
    
    
    func setUpConstraints() {
    ["V","H"].forEach { (quote) -> () in
    let format = String(format:"\(quote):|[nibView]|")
    addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(format, options: [], metrics: nil, views: ["nibView" : nibView]))
    }
    }
    
    
    func loadNib(name: String) -> UIView {
    let bundle = NSBundle(forClass: self.dynamicType)
    let nib = UINib(nibName: name, bundle: bundle)
    let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
    
    
    return view
    }
    

    } < / p >

  5. 在我们的自定义类中,我们还可以定义一些可检查的属性,以便从接口构建器中完全控制它们

    < p > <代码> @IBDesignable class MemeView: XibbedView {

    @IBInspectable var memeImage: UIImage = UIImage() {
    didSet {
    imageView.image = memeImage
    }
    }
    @IBInspectable var textColor: UIColor = UIColor.whiteColor() {
    didSet {
    label.textColor = textColor
    }
    }
    @IBInspectable var text: String = "" {
    didSet {
    label.text = text
    }
    }
    @IBInspectable var roundedCorners: Bool = false {
    didSet {
    if roundedCorners {
    layer.cornerRadius = 20.0
    clipsToBounds = true
    }
    else {
    layer.cornerRadius = 0.0
    clipsToBounds = false
    }
    }
    }
    
    
    @IBOutlet weak var label: UILabel!
    @IBOutlet weak var imageView: UIImageView!
    

}

Few examples:
enter image description here enter image description here

If we need to add more information the view while is displayed inside a storyboard or another xib, to do that we can implement prepareForInterfaceBuilder(), this method will be executed only while opening the file in interface builder. If you did everything I wrote but nothing is working, the is a way to debug a sigle view by adding breakpoints in its implementation. enter image description here
Here is the views hierarchy. View hiearchy

Hope this helps a full sample can be downloaded here

最短的版本:

RSFavoritePlaceholderView *favoritePlaceholderView = [[[NSBundle mainBundle] loadNibNamed:@"RSFavoritePlaceholderView" owner:self options:nil] firstObject];

在Swift 4中以编程方式从nib/xib加载视图:

// Load a view from a Nib given a placeholder view subclass
//      Placeholder is an instance of the view to load.  Placeholder is discarded.
//      If no name is provided, the Nib name is the same as the subclass type name
//
public func loadViewFromNib<T>(placeholder placeholderView: T, name givenNibName: String? = nil) -> T {


let nib = loadNib(givenNibName, placeholder: placeholderView)
return instantiateView(fromNib: nib, placeholder: placeholderView)
}


// Step 1: Returns a Nib
//
public func loadNib<T>(_ givenNibName: String? = nil, placeholder placeholderView: T) -> UINib {
//1. Load and unarchive nib file
let nibName = givenNibName ?? String(describing: type(of: placeholderView))


let nib = UINib(nibName: nibName, bundle: Bundle.main)
return nib
}


// Step 2: Instantiate a view given a nib
//
public func instantiateView<T>(fromNib nib: UINib, placeholder placeholderView: T) -> T {
//1. Get top level objects
let topLevelObjects = nib.instantiate(withOwner: placeholderView, options: nil)


//2. Have at least one top level object
guard let firstObject = topLevelObjects.first else {
fatalError("\(#function): no top level objects in nib")
}


//3. Return instantiated view, placeholderView is not used
let instantiatedView = firstObject as! T
return instantiatedView
}