ChatGPT解决这个技术问题 Extra ChatGPT

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

I'm trying to do something a bit elaborate, but something that should be possible. So here is a challenge for all you experts out there (this forum is a pack of a lot of you guys :) ).

I'm creating a Questionnaire "component", which I want to load on a NavigationContoller (my QuestionManagerViewController). The "component" is an "empty" UIViewController, which can load different views depending on the question that needs to be answered.

The way I'm doing it is:

Create Question1View object as a UIView subclass, defining some IBOutlets. Create (using Interface Builder) the Question1View.xib (HERE IS WHERE MY PROBLEM PROBABLY IS). I set both the UIViewController and the UIView to be of class Question1View. I link the outlets with the view's component (using IB). I override the initWithNib of my QuestionManagerViewController to look like this: - (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.


a
averydev

There is also an easier way to access the view instead of dealing with the nib as an array.

1) Create a custom View subclass with any outlets that you want to have access to later. --MyView

2) in the UIViewController that you want to load and handle the nib, create an IBOutlet property that will hold the loaded nib's view, for instance

in MyViewController (a UIViewController subclass)

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

(dont forget to synthesize it and release it in your .m file)

3) open your nib (we'll call it 'myViewNib.xib') in IB, set you file's Owner to MyViewController

4) now connect your file's Owner outlet myViewFromNib to the main view in the nib.

5) Now in MyViewController, write the following line:

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

Now as soon as you do that, calling your property "self.myViewFromNib" will give you access to the view from your nib!


Will it work for for main controller view (self.view)? For some reasons I need to load main view from nib after standard controller init method. Reason - complex sublassing structure
@Lukasz How much you succeed to do it ?Even I am looking for something like you.
Sorry I might be being thick, but I'm confused at the first point. How do I create outlets in a custom view subclass? I thought outlets were only for UIViewControllers?
What about a case when this view appears on multiple parent views? So do you propose to edit xib for each of them manually? It is TOO BAD!!!
This works only if you want to use the custom nib in a single view controller. In the case where you want to use it on different view controllers, each creating an instance of the custom nib dynamically, this won't work.
g
geedubb

Thank you all. I did find a way to do what I wanted.

Create your UIView with the IBOutlets you need. Create the xib in IB, design it to you liking and link it like this: The File's Owner is of class UIViewController (No custom subclass, but the "real" one). The File Owner's view is connected to the main view and its class is declared as the one from step 1). Connect your controls with the IBOutlets. The DynamicViewController can run its logic to decide what view/xib to load. Once its made the decission, in the loadView method put something like this: 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.


I have a very similar problem - I simply want to load a UIView from an xib file. These steps don't work for me - the app crashes with "bad access" soon after adding the loaded view as a subview.
For iPhone 3.0, use objectAtIndex:0 to get the first element. This crashes for me exactly as described by Justicle. Any idea why?
+1 it worked perfectly for me. I wanted to add multiple views that had the one view controller (im using a flip view scenario where a section of the view spins) I had to make the index on the array 0 (iPhone 3.0)
This is the correct answer, however, the code should be modified so that it loops through nibViews and does a class check on each object, so that way you are certainly getting the correct object. objectAtIndex:1 is a dangerous assumption.
Jasconius is right. You should loop through the nibViews array like this: gist.github.com/769539
D
Dan Rosenstark

I'm not sure what some of the answers are talking about, but I need to put this answer here for when I search in Google next time. Keywords: "How to load a UIView from a nib" or "How to load a UIView from an NSBundle."

Here's the code almost 100% straight up from the Apress Beginning iPhone 3 book (page 247, "Using The New Table View Cell"):

- (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];
} 

This supposes you have a UIView subclass called Blah, a nib called Blah which contains a UIView which has its class set to Blah.

Category: 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

Swift Extension

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
    }
}

And an example in use:

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

If you use the isKindOf you are protected from Bundle structure changes.
An assert would be probably a better solution. This way you will only hide the problem.
@Sulthan an assert is inherently different. I want the object of type Blah wherever it sits in the Nib. I don't care if it's gotten promoted or demoted. However, in response to your comment I've added an assert, but it is not in place of the for loop. It complements it.
You can do the loadFromNib with no parameters: + (id)loadFromNib{ NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil]; for (id object in bundle) { if ([object isKindOfClass:[self class]]) { return object; } } return nil; }
F
Francesco Vadicamo

For all those that need to manage more than one instance of the custom view, that is an Outlet Collection, I merged and customized the @Gonso, @AVeryDev and @Olie answers in this way:

Create a custom MyView : UIView and set it as "Custom Class" of the root UIView in the desired XIB; Create all outlets you need in MyView (do it now because after point 3 the IB will propose you to connect outlets to the UIViewController and not to the custom view as we want); Set your UIViewController as "File's Owner" of the custom view XIB; In the UIViewController add a new UIViews for each instance of MyView you want, and connect them to UIViewController creating an Outlet Collection: these views will act as "wrapper" views for the custom view instances; Finally, in the viewDidLoad of your UIViewController add the following lines:

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


Hi...u know how to do this all programatically ie. not by using xib file at all?
t
thgc

I would use UINib to instantiate a custom UIView to be reused

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

Files needed in this case are MyCustomView.xib, MyCustomViewClass.h and MyCustomViewClass.m Note that [UINib instantiateWithOwner] returns an array, so you should use the element which reflects the UIView you want to re-use. In this case it's the first element.


Don't you mean "customNib" in line 2 instead of "rankingNib"
M
Marc W

You should not be setting the class of your view controller to be a subclass of UIView in Interface Builder. That is most definitely at least part of your problem. Leave that as either UIViewController, some subclass of it, or some other custom class you have.

As for loading only a view from a xib, I was under the assumption that you had to have some sort of view controller (even if it doesn't extend UIViewController, which may be too heavyweight for your needs) set as the File's Owner in Interface Builder if you want to use it to define your interface. I did a little research to confirm this as well. This is because otherwise there would be no way to access any of the interface elements in the UIView, nor would there be a way to have your own methods in code be triggered by events.

If you use a UIViewController as your File's Owner for your views, you can just use initWithNibName:bundle: to load it and get the view controller object back. In IB, make sure you set the view outlet to the view with your interface in the xib. If you use some other type of object as your File's Owner, you'll need to use NSBundle's loadNibNamed:owner:options: method to load the nib, passing an instance of File's Owner to the method. All its properties will be set properly according to the outlets you define in IB.


I'm reading the Apress book, "Beginning iPhone Development: Exploring the iPhone SDK" and they discussed this method at chapter 9: Navigation Controllers and Table Views. It didn't seem too complicated.
I'd be curious to see how they say it's done. I don't have a copy of that book at the moment.
Y
Ying

You can also use UIViewController's initWithNibName instead of loadNibNamed. It is simpler, I find.

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

Now you just have to create MySubView.xib and MySubView.h/m. In MySubView.xib set the File's Owner class to UIViewController and view class to MySubView.

You can position and size of the subview using the parent xib file.


O
Olie

This is a great question (+1) and the answers were almost helpful ;) Sorry guys, but I had a heck of a time slogging through this, though both Gonso & AVeryDev gave good hints. Hopefully, this answer will help others.

MyVC is the view controller holding all this stuff.

MySubview is the view that we want to load from a xib

In MyVC.xib, create a view of type MySubView that is the right size & shape & positioned where you want it.

In MyVC.h, have 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

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.


I used this method, thanks. One change I made to allow it to support nested views was to change [self.view insertSubview: msView aboveSubview: mySubview]; to [mySubview.superview insertSubview: msView aboveSubview: mySubview];
s
skot

This is something that ought to be easier. I ended up extending UIViewController and adding a loadNib:inPlaceholder: selector. Now I can say

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

Here's the code for the category (it does the same rigamarole as described by 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

k
kalafun

In swift

Actually my resolution to this problem was, to load the view in a viewDidLoad in my CustonViewController where I wanted to use the view like that:

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

Don't load the view in a loadView() method! The loadView method serves for loading the view for your custom ViewController.


B
Brynjar

I too wanted to do something similar, this is what I found: (SDK 3.1.3)

I have a view controller A (itself owned by a Nav controller) which loads VC B on a button press:

In AViewController.m

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

Now VC B has its interface from Bnib, but when a button is pressed, I want to go to an 'edit mode' which has a separate UI from a different nib, but I don't want a new VC for the edit mode, I want the new nib to be associated with my existing B VC.

So, in BViewController.m (in button press method)

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

Then on another button press (to exit edit mode):

[editView removeFromSuperview];

and I'm back to my original Bnib.

This works fine, but note my EditMode.nib has only 1 top level obj in it, a UIView obj. It doesn't matter whether the File's Owner in this nib is set as BViewController or the default NSObject, BUT make sure the View Outlet in the File's Owner is NOT set to anything. If it is, then I get a exc_bad_access crash and xcode proceeds to load 6677 stack frames showing an internal UIView method repeatedly called... so looks like an infinite loop. (The View Outlet IS set in my original Bnib however)

Hope this helps.


Reading between the lines, this helped me out too.
According to the information at link you shouldn't manipulate view controllers in this way.
L
Logan

I made a category that I like:

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

Then, call like this:

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

Use a nib name if your nib is named something other than the name of your class.

To override it in your subclasses for additional behavior, it could look like this:

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

A
Andrea

For Swift user with designable option:

Create a custom UIView subclass and a xib files, that we will name after our own class name: in our case MemeView. Inside the Meme View class remember to define it as designable with the @IBDesignable attribute before the class declaration Rember to set the File’s Owner in the xib with our custom UIView subclass in Indetity Inspector panel In the xib file now we can build our interface, make constraints, create outlets, actions etc. We need to implement few methods to our custom class to open the xib once initialized 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 } } In our custom class we can also define some inspecatable properties to have full control over them from interface builder @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!

}

https://i.stack.imgur.com/wW7nG.png

https://i.stack.imgur.com/zUsxd.png

Hope this helps a full sample can be downloaded here


The full article can be see from my blog cloudintouch.it/2016/04/21/designable-xib-in-xcode-hell-yeah but I've already posted the relevant part.
let nib = loadNib(nibName) this is a XibbedView instance, if you call ``` addSubview(nib)``` then you are adding one XibbedView instance on to another XibbedView instance?
i agree on this point. When you try to see it from "Debug View Hierarchy" seems XibbedView contains a XibbedView. You can see it.
@WilliamHu if you donwload the provide example you can see that the MemeView.xib is just a UIView instance with a bunch of subviews, the file holder is a MemeView that is a subclass of XibbedView. Thus the only instance of XibbedView is just MemeView that loads a xib file where the main view is just a view
Oh ok, then. I will test it. Thank you!
M
Meltemi

I found this blog posting by Aaron Hillegass (author, instructor, Cocoa ninja) to be very enlightening. Even if you don't adopt his modified approach to loading NIB files through a designated initializer you will probably at least get a better understanding of the process that's going on. I've been using this method lately to great success!


Link is dead. I believe it is now located at bignerdranch.com/blog/…
B
Barney Mattox

The previous answer does not take into account a change in the NIB (XIB) structure that occurred between 2.0 and 2.1 of the iPhone SDK. User contents now start at index 0 instead of 1.

You can use the 2.1 macro which is valid for all version 2.1 and above (that's two underscores before 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;

We use a technique similar to this for most of our applications.

Barney


Barney: "The previous answer" is meaningless on Stack Overflow because answers shift position based on voting. Better to refer to "Fred's answer" or "Barney's answer" or "olie's answer."
M
MusiGenesis

I had reason to do the same thing (programmatically loading a view from a XIB file), but I needed to do this entirely from the context of a subclass of a subclass of a UIView (i.e. without involving the view controller in any way). To do this I created this utility method:

+ (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;
} 

Then I call it from my subclass' initWithFrame method like so:

- (id)initWithFrame:(CGRect)frame {

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

Posted for general interest; if anyone sees any problems without doing it this way, please let me know.


I've implemented your code, but it keeps going out of scope on init. I made your static method in a Utilities class. I made h/m files called MyView (subclassed from UIView) and a xib named the same. In I.B. I set the xib files owner AND the view to "MyView." In the debugger it's out of scope. Am I missing something in I.B.?
Please have a look at my new S.O. question here: stackoverflow.com/questions/9224857/…
self = [Utilities initWithNibName:@"XIB1" withSelf:self]; You're passing self when it's not set yet. Isn't that code the same as this: self = [Utilities initWithNibName:@"XIB1" withSelf:nil]; ?
@Javy: my code sample here may not work with ARC - I have not tested it with ARC enabled.
J
Johannes

Here's a way to do it in Swift (currently writing Swift 2.0 in XCode 7 beta 5).

From your UIView subclass that you set as "Custom Class" in the Interface Builder create a method like this (my subclass is called 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
}

Then you can just call it like this:

let recordingFooterView = RecordingFooterView.loadFromNib()


M
Matt

@AVeryDev

6) To attach the loaded view to your view controller's view:

[self.view addSubview:myViewFromNib];

Presumably, it is necessary to remove it from the view to avoid memory leaks.

To clarify: the view controller has several IBOutlets, some of which are connected to items in the original nib file (as usual), and some are connected to items in the loaded nib. Both nib's have the same owner class. The loaded view overlays the original one.

Hint: set the opacity of the main view in the loaded nib to zero, then it won't obscure the items from the original nib.


good thing to watch out for, but adding a view as a subview automatically removes it from its previous parent so it shouldn't leak.
s
soumya

None of the answers explain how to create the stand alone XIB that is the root of this question. There is no Xcode 4 option to "Create New XIB File".

To do this

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

This may seem simple but it can save you a few minutes poking around for it since the word "XIB" does not appear anywhere.


A
Aamir

After spending many hours, I forged out following solution. Follow these steps to create custom UIView.

https://i.stack.imgur.com/dZXFq.png

https://i.stack.imgur.com/vxSnf.png

https://i.stack.imgur.com/enKtV.png

https://i.stack.imgur.com/mGoBT.png

5) Load .xib in myCustomView * customView. Use following sample code.

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


D
Denis Kutlubaev

Shortest version:

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

n
n13

I have a convention of naming xibs with views in them the same as the view. Same as one would do for a view controller. Then, I don't have to write out class names in code. I load a UIView from a nib file with the same name.

Example for a class called MyView.

Create a nib file called MyView.xib in Interface Builder

In Interface Builder, add a UIView. Set its class to MyView. Customize to your heart's content, wire up instance variables of MyView to subviews you might want to access later.

In your code, create a new MyView like this: MyView *myView = [MyView nib_viewFromNibWithOwner:owner];

Here's the category for this:

@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 {

}

I'd then wire up buttons with actions from the controller I am using, and set things on labels using the outlets in my custom view subclass.


E
Eng Yew

To programmatically load a view from a nib/xib in Swift 4:

// 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
}

N
NANNAV

I ended up adding a category to UIView for this:

 #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

explanation here: viewcontroller is less view loading in ios&mac


Hrm. What's with the tag?
to know which top level object to retain. at the time it was necessary, but you could probably take out the retains to comply with ARC now.

关注公众号,不定期副业成功案例分享
Follow WeChat

Success story sharing

Want to stay one step ahead of the latest teleworks?

Subscribe Now