Load Nib for UIView used in ViewController in StoryBoard - uiview

I have a UIView that I want to use in several View Controllers so I created the view (.h/.m) and a nib file for it. In Storyboard view is added as subview to view controller but is not loading the layout as defined in nib.
Is it possible to load the nib for a UIView used within a view controller in a Storyboard?

If you ask about seeing your .xib content in Interface Builder when editing Storyboard, it's not possible. But if you need to load it in runtime, it's possible.
.h/.m should contain UIView subclass
in .xib file you need to set File's owner (Identity Inspector -> Custom Class) with your custom UIView subclass
in .h file add #property (strong, nonatomic) IBOutlet UIView* view; and connect your View container from Interface Builder to this field (Ctrl + Click on View and drag connection to above view property in .h
connect other subviews to .h
in .m file you need to place
- (void)awakeFromNib {
NSString* nibName = #"Place here your Nib name without extension";
if ([[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil]) {
[self.view setFrame:[self bounds]];
[self addSubview:self.view];
}
}
That's all. Move View to Storyboard scene, change Custom Class to your FooView, import .h in View Controller code and use IBOutlets as you want.
Have happy coding
EDIT
I want to notice that according awakeFromNib documentation we should avoid such code in awakeFromNib.
It is recommended that you maintain a one-to-one correspondence between your File’s Owner objects and their associated nib files. Loading two nib files with the same File’s Owner object causes that object’s awakeFromNib method being called twice, which could cause some data structures to be reinitialized in undesired ways. It is also recommended that you avoid loading other nib files from your awakeFromNib method implementation.

Name your view ViewInNib, then we have:
ViewInNib.h
ViewInNib.m
ViewInNib.xib
In your storyboard, put a placeholder UIView component in the view controller, remember to set it's class ViewInNib, then in ViewInNib.m:
static BOOL isReplaced = NO;
- (id)awakeAfterUsingCoder:(NSCoder *)aDecoder
{
if (!isReplaced)
{
isReplaced = YES;
ViewInNib *replaced = [[[NSBundle mainBundle] loadNibNamed:#"ViewInNib" owner:nil options:nil] lastObject];
replaced.frame = self.frame;
replaced.translatesAutoresizingMaskIntoConstraints = NO;
return replaced;
}
isReplaced = NO;
return self;
}
In this method, we replace real view loaded from nib from view in storyboard. Hope it helps.

use XXNibBridge:
https://github.com/sunnyxx/XXNibBridge
Super simple to use and works with Swift as well.
Full credit goes to #sunnyxx

Related

Why does custom xib require UIView property

I followed the tutorial outlined here to load a custom xib in my view controller.
The class of the xib inherits from UIView but also needs a property view:
#interface MYBannerView : UIView
#property (nonatomic, weak) IBOutlet UIView *view;
#end
I find it strange that it needs this, as its like having a view within a view which seems redundant. Is there any particular reason for this?
Edit
I followed this tutorial here which outlines this:
http://www.maytro.com/2014/04/27/building-reusable-views-with-interface-builder-and-auto-layout.html
The author of that tutorial is using the view outlet to load the view from the xib when MYBannerView is initialized programatically
- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
NSString *className = NSStringFromClass([self class]);
self.view = [[[NSBundle mainBundle] loadNibNamed:className owner:self options:nil] firstObject];
[self addSubview:self.view];
return self;
}
return nil;
}
Anything created in Interface Builder will not be loaded with a programatic init. loadNibNamed loads the UI from the xib.
If you want to override initWithFrame: to load the UI elements from the xib, your init method would look something like this:
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame]; // initialize your objects
if (self) {
[[NSBundle mainBundle] loadNibNamed:NSStringFromClass([self class]) owner:self options:nil]; // load your view from IB
}
return self; // return the view with all of the IB UI loaded
}
No, your UIView subclass doesn't need an outlet to the view to be functional. In fact, using self you have access to the view itself.
What about tutorial: when author added view A (let's call it so) on MYViewController's view, he set view A class to be MYBannerView. Running app won't show anything because MYBannerView xib wasn't loaded and didn't replaced content from view A. So author loads this xib in initWithCoder, set outlet using value returned by loadNibNamed: and adds view loaded from MYBannerView to view A. The process is a little bit messy but it works.

iOS: Simple UIVIew not getting added to another UIView

I have a storyboard driven iOS application.
My objective is to add a small square view (defined as other XIB) to another UIView which is on main ViewController.
*// Please note that, I have assigned the Class names in Identity Inspector correctly*
On the main ViewController, I added a UIView (myHolderView) manually and added a UIView member in the ViewController interface.
//.h
#interface ViewController : UIViewController
#property(nonatomic, strong) IBOutlet UIView *myHolderView;
#end
And, I have connected this IBOutlet by "BlueString" :)
Now, I have added another XIB with single view - Called it MySquare.xib and added a label on it.
And, added a UIView class with a .h and .m files. And, "BlueString" connections are made.
#interface MySquare : UIView
#property(nonatomic, strong) IBOutlet UILabel *myLabel;
#end
Now, in the implementation:
#implementation ViewController
#synthesize myHolderView;
- (void)viewDidLoad
{
[super viewDidLoad];
MySquare *sq=[[MySqure alloc]init];
// Do any additional setup after loading the view, typically from a nib.
[self.myHolderView addSubview:sq];
}
When I run it in the simulator, it just shows the main view, but on it the square view is not coming.
I know that main view had loaded because... I changed the colour of it so that I could see.
Please help.
Probably you don't do in your MySquare.m file the view initialisation using your xib.
- (id) initWithFrame:(CGRect)frame {
NSLog(#"This is called if you add programatically this view");
self = [super initWithFrame:frame];
if (self) {
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:#"MyIBView" owner:self options:nil] objectAtIndex:0]];
}
return self;
}
If you plan to add your xib using Interface Builder, you have to implement similarly the initWithCoder method:
-(id)initWithCoder:(NSCoder *)aDecoder {
if ((self = [super initWithCoder:aDecoder])) {
[self addSubview:[[[NSBundle mainBundle] loadNibNamed:#"MyIBView" owner:self options:nil] objectAtIndex:0]];
}
return self;
}
Plese go through in this checklist, it might help others too:
Checklist adding a custom view with Interface Builder layout (xib):
1) create a new xib (File->New->IOS/User Interface->View), name it (eg. MyIBView)
2) create a new class (File->New->IOS/Cocoa Touch->Objective-C class), make sure it is a Subclass of UIView! Name it MyIBView.
Now you have MyIBView.xib, MyIBView.h and MyIBView.m files added to your project
3) Edit your MyIBView.xib: select the File's owner and using the Identity inspector (3rd icon) select Class you created recently, MyIBView.
4) Select the top level View (if its a new xib, only this exists) and make sure using the Identity inspector that the Class is UIView (grey) and not overwritten
5) Now you can add IBOutlets and/or IBActions to your MyIBView.h file and you can do connections using Interface Builder in your MyIBView.xib
Using this view programatically in another view:
6a) Create the first method (initWithFrame) above in your MyIBView.m file if you would like to use this custom view programatically
7a) include your MyIBView.h file where you would like to add your custom view programatically
8a) now you can add your view:
MyIBView *myIBView = [[MyIBView alloc] initWithFrame:CGRectMake(0,0,100,100)];
Using this view in Interface Builder in another view:
6b) Create the second method (initWithCoder) above in your MyIBView.m file if you would like to use this custom view in Interface Builder
7b) In your another view drop an UIView to your view canvas. Adjust the size and modify the Class using the Identity inspector to MyIBView.
Please note that you cannot see design time your custom view, only after trying to run the code.
Hope it helps, and you can find your miss-configuration!

confused difference between Custom Class for an Object and for the File's Owner and steps via IB

This is another "I'm confused question". So I'm working on bringing in custom views into a view controller. I'll just outline the exact steps for the error.
Create a Single View Application Project
Create a Nib File via File -> New -> User Interface -> View; call it theNIB.xib. Add a simple label to make sure loading.
add following code:
UIView *view = [[[NSBundle mainBundle] loadNibNamed:#"theNIB" owner:self options:nil] objectAtIndex:0];
view.frame=CGRectMake(10.0f,10.0f,100.0f,100.0f);
view.backgroundColor=[UIColor orangeColor];
[self.view addSubview:view];
This works.
Now what I want to do is connect this nib with a UICustomView so I create ArcView via File -> New -> UIView.
'4. In IB, I need to connect theNIB to ArcView so I highlight the File's Owner in the Placeholders and select AcrView in the Custom Class.
'5.' Then I select the main View and set it to ArcView in the Custom Class.
I am at a loss for what the next step is or whether 4 or 5 were necessary (both / neither)? Do I try to make an outlet reference in ArcView to view for the main View in Interface Builder? Should I be able to Alt-drag from the View to the header file in assistant editor (I am not currently able to)?
thx in advance
** edit 1 **
Here's the File's Owner with arcView set:
View object with arcView not set:
Define an IBOutlet #property in your parent class's #interface section like this:
#property (weak, nonatomic) IBOutlet ArcView *arcView
Then go into Interface Builder, and right click on File's Owner. When you see "arcView" in the black HUD window, drag the mouse from that item to your view on the XIB.
Now you have a property for your arcview control, and you can utilize it just like you would any control such as UIButton, UILabel etc.
Set the file's owner == your UIView subclass so that you can connect outlets to it. And you should set the class of nib-painted UIView to that same subclass because it's an instance of that UIView subclass.
In other words, follow these steps:
create a UIView subclass called CustomView
create a UIView xib New File -> User Interface -> View
change the file's owner to CustomView
change the view's class to CustomView
add subviews if you wish, connect them as outlets to the files owner (CustomView)
Your crash is happening because your code says this:
UIView *view = [[[NSBundle mainBundle] loadNibNamed:#"theNIB" owner:self options:nil] objectAtIndex:0];
But that owner:self is the view controller where this code is running. You want the view subclass to be the nib's owner.
To fix, give your UIView subclass the job of init-ing itself from the nib, like this:
CustomView.h
#interface CustomView : UIView
- (id)initFromNib;
#end
CustomView.m
#import "CustomView.h"
#interface CustomView ()
// connect this in the XIB to file's owner that you've set to this CustomView class
#property (weak, nonatomic) IBOutlet UILabel *myLabel;
#end
#implementation CustomView
- (id)initFromNib
{
self = [[[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self options:nil] objectAtIndex:0];
if (self) {
// prove you can set properties on your outlets
self.myLabel.text = #"this is good";
}
return self;
}
I built a little project with just this stuff in it as described. Works fine. Lemme know if you'd like to see it and I'll find a way to send you an anonymous zip.

How can I load nib from UIViews in iOS 4?

How can I load nib from a UIView in iOS 4 ?
I don't have a UIViewController, since I'm adding this custom view to a parent view.
I want to initialize this custom view from a nib file. Which initializer should I use ? initWithFrame or initWithCoder ?
So far, I've only assigned the custom class in the interface builder. (See pic: http://cl.ly/7pmj). Is this necessary ? If so, it is still not working, why is it not enough ?
NB I've found this related question in StackOverflow. However, it doesn't explain how to initialize the view itself with the nib.
I need to customize drawRect method, for this view, so I can't just add a subview from a nib file. I need the view itself being initialized from the nib file.
thanks
Yes, it's a must to change the custom view class in your nib file.
Make sure two things:
1. Go to yourCustomView.nib file, select the view in object, then make sure the custom class is correct under the identity inspector.
2. In the youCustomView.m file, make sure you have this part:
- (id)initWithCoder:(NSCoder*)coder
{
if ((self = [super initWithCoder:coder]))
{
//add custom change after init from nib.
}
return self;
}
Use the method from the related question (NSBundle loadNibNamed) and make sure the owner in that call is an object that has an IBOutlet for the view in question.
Hook that iboutlet up to the view in interface builder (you will need to set the class of the owner in IB to the class of the owner in your load nib call).
The nib will load the view into that iboutlet variable.
In .h file:
IBOutlet UIView *myView;
in .m file:
[[NSBundle mainBundle] loadNibNamed:#"viewToLoad" owner:self options:nil];
[someOtherView addSubView:myView];
The rest is in IB.

UIView and initWithFrame and a NIB file. How can I get the NIB file loaded?

I have a UIView called baseViewand in that I have initWithFramewhere I add some other views and do some custom stuff. The same view also has a NIB file.
Now I have a UIViewController class named AppController in which I want to add the baseView view to the view of the AppController view so I am doing this:
self.view = baseView; but the problem is that the NIB file does not get loaded. How do I make sure the customized stuff AND the NIB file get´s loaded/run?
You have many options, depending on how your "baseView" class is meant to be used and integrated in to your application. It's not clear just how you intend to use this class -- as the view in a UIViewController subclass, or as a reusable modular component mean to be instantiated multiple times throughout your application, for use in many different view controllers.
If your view is meant to be the only view in a UIViewController subclass, then Phonitive is correct -- bundle it together with the UIViewController subclass .xib file and use the UIViewController's viewDidLoad to do final initialization.
But if you want your View class to be a subcomponent reused multiple times in different view controllers, integrated either via code or via inclusion in a .xib file for another controller, then you need to implement both the initWithFrame: init method, and awakeFromNib, to handle both cases. If your internal initialization always includes some objects from .xib, then in your initWithFrame you'll need to load your .xib manually in order to support "customer" classes that want to create your widget via code. And likewise, if a .xib file contains your object then you'll need to make sure you call any code-required finalization from awakeFromNib.
Here's an example of how to create a UIView subclass component with the UI design in a nib.
MyView.h:
#interface MyView : UIView
{
UIView *view;
UILabel *l;
}
#property (nonatomic, retain) IBOutlet UIView *view;
#property (nonatomic, retain) IBOutlet UILabel *l;
MyView.m:
#import "MyView.h"
#implementation MyView
#synthesize l, view;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code.
//
[[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil];
[self addSubview:self.view];
}
return self;
}
- (void) awakeFromNib
{
[super awakeFromNib];
// commenters report the next line causes infinite recursion, so removing it
// [[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil];
[self addSubview:self.view];
}
- (void) dealloc
{
[l release];
[view release];
[super dealloc];
}
Here's what the nib file looks like (except that file's owner needs to be changed to MyView class).
be sure to hook up both the view and label outlets to File's Owner. That's it! A template for creating re-usable UIView widgets.
The really neat thing about this structure is that you can place instances of your MyView object in other nib files, just place a UIView at the location/size you want, then change the class in the identity inspector (CMD-4) to MyView, and boom, you've got an instance of your widget in whatever views you want! Just like UIKit objects you can implement delegate protocols so that objects using your widget can be notified of interesting events, and can provide data to display in the widget to customize it.
I found this post after having a problem trying to do this in my app. I was trying to instantiate the view from a NIB in the ViewDidLoad method, but the controls acted as if they were disabled. I struggled with this trying to directly set the userInteractionEnabled property and programmatically set the touch event selector for a button in this view. Nothing worked. I stumbled upon another post and discovered that viewDidLoad was probably too soon to be loading this NIB. I moved the load to the ViewWillAppear method and everything worked. Hope this helps someone else struggling with this. The main response was great and works well for me now that I have it being called from the proper place.
if you want to use a NIB, it's better for your UIView to be linked with a UIViewController, in this case you can use
UIViewController *vc=[[UIViewController alloc] initWithNibName:#"YourNIBWihtOUTEXTENSION" bundle:nil]
[self.view addSubView:vc.view ];
becareful of memory leaks, you have to release vc
If you have a custom UIView with a xib file.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
id mainView;
if (self)
{
NSArray *subviewArray = [[NSBundle mainBundle] loadNibNamed:#"HomeAllAdsView" owner:self options:nil];
mainView = [subviewArray objectAtIndex:0];
}
return mainView;
}
- (void) awakeFromNib
{
[super awakeFromNib];
}
This post helped me Building Reusable Views With Interface Builder and Auto Layout. The trick had to do with setting the IBOutlets to the FileOwner and then adding the content view to itself after loading the nib

Resources