关于iphone:如何使用Interface Builder创建的nib文件加载UIView

How to load a UIView using a nib file created with Interface Builder

我想做些有点复杂的事情,但应该是可能的。所以这对你们所有的专家来说都是一个挑战(这个论坛是你们很多人组成的)。

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

我的做法是:

  • 创建question1view对象作为UIView子类,定义一些IBOutlets
  • 创建(使用接口生成器)Question1View.xib(这里可能是我的问题所在)。我将UIViewControllerUIView都设置为类问题1视图。
  • 我将插座与视图的组件链接(使用ib)。
  • 我重写了我的QuestionManagerViewControllerinitWithNib,如下所示:

    1
    2
    3
    4
    5
    6
    - (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
        if (self = [super initWithNibName:@"Question1View" bundle:nibBundleOrNil]) {
            // Custom initialization
        }
        return self;
    }
  • 当我运行代码时,我得到这个错误:

    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.'

    我确信有一种方法可以使用NIB文件加载视图,而不需要创建一个viewController类。


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

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

    2)在要加载和处理NIB的uiviewController中,创建一个IBoutlet属性来保存加载的NIB的视图,例如

    在MyViewController(uiViewController子类)中

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

    (别忘了合成它并在.m文件中释放它)

    3)在ib中打开nib(我们称之为myviewnib.xib),将文件的所有者设置为myviewcontroller。

    4)现在将文件的所有者outlet myviewfromnib连接到nib中的主视图。

    5)现在在MyViewController中,编写以下行:

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

    现在,一旦您这样做,调用您的财产"self.myviewfromnib"将允许您从您的nib访问视图!


    谢谢大家。我确实找到了一种做我想做的事的方法。

  • 用你需要的IBOutlet创建你的UIView
  • 在ib中创建xib,根据您的喜好进行设计,并按如下方式链接:文件的所有者是类UIViewController(没有自定义子类,但是是"真正的"子类)。文件所有者的视图连接到主视图,其类声明为步骤1中的类)。
  • 将控制装置与IBOutlet连接。
  • DynamicViewController可以运行其逻辑来决定要加载什么视图/xib。一旦做出决定,在loadView方法中,将如下所示:

    1
    2
    3
    4
    5
    6
    7
    NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:@"QPickOneView"
                                                      owner:self
                                                    options:nil];

    QPickOneView* myView = [ nibViews objectAtIndex: 1];

    myView.question = question;
  • 就是这样!

    主包的loadNibNamed方法将负责初始化视图和创建连接。

    现在,视控器可以根据内存中的数据显示一个或另一个视图,而"父"屏幕不需要担心这种逻辑。


    我不知道其中的一些答案是什么,但下次在谷歌搜索时,我需要把这个答案放在这里。关键词:"如何从NIB加载uiview"或"如何从nsbundle加载uiview"。

    这是几乎100%直接从一本iphone 3开始的书(第247页,"使用新的表视图单元")开始的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    - (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的笔尖,其中包含一个UIView,它的类设置为Blah

    类别:nsObject+loadfromnib

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    #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

    快速扩展

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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
        }
    }

    以及一个正在使用的示例:

    1
    2
    3
    4
    5
    class SomeView: UIView {
        class func loadFromNib() -> SomeView? {
            return self.loadFromNib(withName:"SomeView")
        }
    }


    对于需要管理多个自定义视图实例(即outlet集合)的所有人,我合并并自定义了@gonso、@averydev和@olie,以这种方式回答:

  • 创建一个自定义MyView : UIView并将其设置为所需xib中根UIView的"自定义类";custom class

  • 创建您在MyView中需要的所有插座(现在就做,因为在第3点之后,ib将建议您将插座连接到UIViewController,而不是我们想要的自定义视图);custom class outlet

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

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

  • 最后,在您的UIViewControllerviewDidLoad中添加以下行:

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    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..


    我将使用uinib实例化要重用的自定义uiview

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

    这种情况下需要的文件是mycustomview.xib、mycustomviewclass.h和mycustomviewclass.m请注意,[UINib instantiateWithOwner]返回一个数组,因此应该使用反映要重用的uiview的元素。在本例中,它是第一个元素。


    您不应该将视图控制器的类设置为接口生成器中UIView的子类。这至少是你问题的一部分。将其保留为UIViewController、它的某些子类或您拥有的其他自定义类。

    至于只从XIB加载视图,我假设您必须在Interface Builder中将某种类型的视图控制器(即使它不扩展UIViewController,这可能对您的需求来说太重)设置为文件的所有者(如果您想使用它来定义接口)。我也做了一些研究来证实这一点。这是因为,否则将无法访问UIView中的任何接口元素,也不会有方法让您自己的代码方法由事件触发。

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


    也可以使用uiviewController的initWithNibName而不是loadNibNamed。我发现这很简单。

    1
    2
    3
    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,将view类设置为mysubview。

    您可以使用父XIB文件定位和调整subview的大小。


    这是一个很好的问题(+1),答案几乎是有用的;)抱歉,伙计们,但我花了很长时间苦苦研究这个问题,尽管冈索和艾弗里德夫都给出了很好的提示。希望这个答案能帮助其他人。

    MyVC是控制所有这些内容的视图控制器。

    MySubview是我们希望从XIB加载的视图

    • 在myvc.xib中,创建一个MySubview类型的视图,该视图的大小和形状与所需位置相匹配。
    • 在myvc.h中,有

      1
      2
      3
      IBOutlet MySubview *mySubView
      // ...
      @property (nonatomic, retain) MySubview *mySubview;
    • 在myvc.m中,@synthesize mySubView;,不要忘记在dealloc中释放它。

    • 在mysubview.h中,为UIView *view拥有一个出口/财产(可能不需要,但对我有效)。在.m中合成并发布它。
    • 在mysubview.xib中
      • 将文件所有者类型设置为MySubview,并将视图属性链接到视图。
      • 根据需要布置所有位并连接到IBOutlet
    • 回到myvc.m,有

      1
      2
      3
      4
      5
      6
      7
      8
      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];

    对我来说,棘手的一点是:其他答案中的提示从XIB加载了我的视图,但没有替换myvc中的视图(duh!)--我必须自己把它换掉。

    此外,要访问MySubview的方法,.xib文件中的view属性必须设置为MySubview属性。否则,它会恢复为一个普通的老UIView

    如果有办法直接从它自己的XIB加载MySubview,那就太棒了,但这让我找到了我需要的地方。


    这应该更容易些。最后我扩展了UIViewController并添加了loadNib:inPlaceholder:选择器。现在我可以说

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

    这是该类别的代码(它执行与Gonso描述的相同的rigamarole):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    @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


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

    UIView+NibInitializer.h

    1
    2
    3
    4
    5
    #import <UIKit/UIKit.h>

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

    UIView+NibInitializer.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #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

    然后,这样叫:

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

    如果您的笔尖不是类名称,请使用笔尖名称。

    要在子类中重写它以获得其他行为,它可能如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    - (instancetype)initWithNibNamed:(NSString *)nibNameOrNil
    {
        self = [super initWithNibNamed:nibNameOrNil];
        if (self) {
            self.layer.cornerRadius = CGRectGetHeight(self.bounds) / 2.0;
        }
        return self;
    }


    在斯威夫特

    实际上,我对这个问题的解决方案是,将视图加载到我的custonviewcontroller中的viewdidLoad中,在这里我想使用这样的视图:

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

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


    我也想做类似的事情,这就是我发现的:(SDK 3.1.3)

    我有一个视图控制器A(它本身属于导航控制器),它在按钮上加载VC B,按:

    在aviewcontroller.m中

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

    现在vc b有了b nib的接口,但是当按下一个按钮时,我想进入一个"编辑模式",它有一个独立的用户界面,来自不同的nib,但是我不想在编辑模式下有一个新的vc,我想新的nib与我现有的bvc相关联。

    所以,在bview controller.m中(在button press method中)

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

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

    1
    [editView removeFromSuperview];

    我回到了我原来的BNIB。

    这很好,但是注意我的editmode.nib只有一个顶级obj,一个uiview obj。不管文件在这个NIB中的所有者是设置为bView控制器还是默认nsObject,但要确保文件所有者中的视图出口没有设置为任何内容。如果是,那么我会得到一个exc-bad-access崩溃,xcode继续加载6677个堆栈帧。显示重复调用的内部uiview方法…所以看起来像一个无限循环。(视图插座设置在我原来的bnib中)

    希望这有帮助。


    对于具有可指定选项的Swift用户:

  • 创建一个自定义的UIView子类和一个xib文件,我们将用自己的类名命名:在我们的例子中是memeview。在meme view类中,记住在类声明之前用@IBDesignable属性将其定义为可设计的。
  • 请记住在XIB中使用Indentity Inspector面板中的自定义UIView子类设置文件的所有者。

    enter image description here

  • 在XIB文件中,我们现在可以构建接口、进行约束、创建出口、操作等。enter image description here

  • 我们需要对自定义类实现一些方法,以便在初始化后打开XIB

    class XibbedView: UIView {

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    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
    }

    }

  • 在我们的自定义类中,我们还可以定义一些可检查的属性,以便从接口生成器对它们进行完全控制。

    @IBDesignable
    class MemeView: XibbedView {

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    @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!
  • }

    举几个例子:enter image description hereenter image description here

    如果我们需要在故事板或其他XIB中显示视图时添加更多信息,为了实现这一点,我们可以实现prepareForInterfaceBuilder(),只有在Interface Builder中打开文件时才会执行此方法。如果您做了我写的所有事情,但没有任何工作,那么可以通过在实现中添加断点来调试Sigle视图。enter image description here这是视图层次结构。View hiearchy

    希望这有助于在这里下载完整的示例


    我发现Aaron Hillegas(作者、导师、可可忍者)的博客帖子非常有启发性。即使您不采用他修改过的方法通过指定的初始值设定项加载NIB文件,您至少可以更好地理解正在进行的过程。我最近用这种方法取得了巨大的成功!


    这里有一种使用swift的方法(目前在Xcode7测试版5中编写swift 2.0)。

    从您在接口生成器中设置为"自定义类"的UIView子类创建这样的方法(我的子类称为recordingfooterview):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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()


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

    您可以使用2.1宏,该宏适用于所有2.1及以上版本(iPhone之前有两个下划线:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     // 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;

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

    巴尼


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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    + (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;
    }

    然后我从我的子类"EDOCX1"〔17〕方法中调用它,如下所示:

    1
    2
    3
    4
    5
    6
    7
    8
    - (id)initWithFrame:(CGRect)frame {

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

    为了大家的兴趣张贴;如果有人看到任何问题没有这样做,请告诉我。


    这些答案都不能解释如何创建这个问题的根源——独立XIB。没有"创建新的XIB文件"选项。

    这样做

    1
    2
    3
    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"这个词在任何地方都不会出现。


    @ AVeryDev

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

    1
    [self.view addSubview:myViewFromNib];

    可能需要将其从视图中移除,以避免内存泄漏。

    为了澄清:视图控制器有几个iboutlet,其中一些连接到原始NIB文件中的项目(和往常一样),而另一些连接到加载的NIB中的项目。两个NIB具有相同的所有者类。加载的视图覆盖原始视图。

    提示:将加载的NIB中主视图的不透明度设置为零,这样就不会模糊原始NIB中的项目。


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

    1)创建从uiview继承的类mycustomview。enter image description here

    2)创建名为MyCustomView的.xib。enter image description here

    3)更改.xib文件中uiview的类,在那里分配mycustomview类。enter image description here

    4)创建IBoutletsenter image description here

    5)在mycustomview*customview中加载.xib。使用以下示例代码。

    1
    2
    myCustomView * myView = [[[NSBundle mainBundle] loadNibNamed:@"myCustomView" owner:self options:nil] objectAtIndex:0];
    [myView.description setText:@"description"];

    Note: For those who still face issue can comment, I will provide them link
    of sample project with myCustomView


    最短版本:

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

    我有一个命名XIB的惯例,其中的视图与视图相同。和视图控制器一样。然后,我不必用代码写出类名。我从具有相同名称的NIB文件加载uiview。

    名为MyView的类的示例。

    • 在Interface Builder中创建名为myview.xib的NIB文件
    • 在Interface Builder中,添加一个uiview。将其类设置为MyView。根据您的心脏内容进行定制,将MyView的实例变量连接到您以后可能要访问的子视图。
    • 在代码中,创建一个新的myview,如下所示:

      myview*myview=[myview nib_viewfromnibwithowner:owner];

    以下是此项的类别:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    @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 {

    }

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


    要以编程方式从swift 4中的NIB/XIB加载视图:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    // 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
    }

    我最终为以下内容向uiview添加了一个类别:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     #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

    此处说明:VIEWController在iOS&mac中的视图加载较少