Reusable UIView as a nib file - iphone

I created a Custom UIView called BOHeaderView using xib file. I want this BOHeaderView to be loaded in other UIViewController' xibs.
I tried by adding a UIView in one ViewController nib file and change its type to customView. But I am not able to load the custom view. Below is the initialization code of customView.
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Custom initialization
[[NSBundle mainBundle] loadNibNamed:#"BOHeaderView" owner:self options:nil];
}
return self;
}
- (void) awakeFromNib
{
[super awakeFromNib];
// commenters report the next line causes infinite recursion, so removing it
[self customizeHeadView];
}

You can do it like this :
NSArray *nibViews = [[NSBundle mainBundle] loadNibNamed:#"BOHeaderView"
owner:[[BOHeaderView alloc] init]
options:nil];
BOHeaderView *infoView = [nibViews objectAtIndex:0];
infoView.frame = CGRectMake( put your rect here );
[self addSubview:infoView];
EDIT :
Means you are trying to say : Using a custom UIView from multiple view controllers
I had the same problem and I solved it in this way :
I wrote the below code on buttonClick.
RatingView *rateView = [[RatingView alloc] initWithNibName:#"RatingView" bundle:[NSBundle mainBundle]];
rateView.view.frame = CGRectMake(0,45,1024, 648);
[self.view addSubview:rateView.view];
Happy Coding !!

That row with the NSBundle should be in the class in which you want to load the nib. You should write in that class:
CustomView *customView = [[NSBundle mainBundle] loadNibNamed:#"BOHeaderView" owner:self options:nil].lastObject;
It returns an NSArray, that is why you need the lastObjct

Related

Remove Custom View from superView

I have My own UIView :
#import <UIKit/UIKit.h>
#interface MultipleSlotsClientView : UIView
-(IBAction)didPressCloseBtn:(id)sender;
#end
And this is the implementation:
#implementation MultipleSlotsClientView
- (id)initWithFrame:(CGRect)frame {
self = [[[[NSBundle mainBundle] loadNibNamed:#"MultipleSlotsClientView" owner:self options:nil] objectAtIndex:0] retain];
if (self) {
self.frame = frame;
}
return self;
}
#pragma mark
#pragma mark IBAction
-(IBAction)didPressCloseBtn:(id)sender {
[self removeFromSuperview];
}
#end
And i have a btn that connect to the didPressCloseBtn method and when i press the button the method called but the View won't remove from the superview.
This is how i alloc the UIView and add it:
MultipleSlotsClientView *multiView = [[[MultipleSlotsClientView alloc] initWithFrame:self.view.frame] autorelease];
[self.view addSubview:multiView];
Any idea why the view won't disappear?
Just try to connect like below screenshot, don't connect to FileOwner.
Step-1 write the below method in NSObject Category class
+ (id)loadNibNamed:(NSString *)NibName {
NSObject *cl = nil;
if (NSClassFromString(NibName) != nil) {
NSArray *arr = [[NSBundle mainBundle] loadNibNamed:NibName owner:self options:nil];
for (id cls in arr) {
if([cls isKindOfClass:NSClassFromString(NibName)])
{
cl = cls;
break;
}
}
}
return cl;
}
Step:2 call the respective Class like
MultipleSlotsClientView *multiView = [MultipleSlotsClientView loadNibNamed:#"MultipleSlotsClientView"]
[self.view addSubview:multiView];
And No need to write anything in "initWithFrame".
Try this. It may work for you.
This is an answer to a comment. Just because you cannot format comments nicely. It is about why you leak memory in your code and how you could write the code to overcome the issue.
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Do something here if you have to.
// Currently there is no reason for overwriting intiWithFrame: at all.
}
return self;
}
And instead of:
MultipleSlotsClientView *multiView = [[[MultipleSlotsClientView alloc] initWithFrame:self.view.frame] autorelease];
Just do:
MultipleSlotsClientView *multiView= [[[[NSBundle mainBundle] loadNibNamed:#"MultipleSlotsClientView" owner:self options:nil] objectAtIndex:0] autorelease];
multiView.frame = self.view.frame;
Well, whether you should retain or autorelease it or nothing of that kind at all depends on the other code. Assuming that you add multiView to the subview hierarchy, which will retain it, autorelease is fine.

Why does this init method when loading a nib return an object out of scope?

Why does this init method return an object out of scope?
Using XCode 4.2, base SDK of 4.3, and ARC, I'm trying to load an UIView from a nib (not a UIViewController). I need to not use a UIViewController at all in the process.
After reading this answer to an S.O. question here, it looks like it can be done:
How to load a UIView using a nib file created with Interface Builder
(The answer by user "MusiGenesis" describes the process)
I created a sub-class of UIView with a single label:
#interface MyView : UIView
#property (unsafe_unretained, nonatomic) IBOutlet UILabel *textLabel;
#end
In the implementation I override initWithFrame:
- (id)initWithFrame:(CGRect)frame
{
//self = [super initWithFrame:frame];
self = [JVUIKitUtils initWithNibName:#"MyView" withOwner:self];
if( self )
{
NSLog(#"Created");
}
return self;
}
In I.B. I created a file named "MyView.xib" with a single view. It has a label as a sub-view, and I created the label property by dragging it to the h file.
And in another file, I created this re-usable static method:
+ (id)initWithNibName:(NSString*)nibName withOwner:(id)uiView
{
id object = nil;
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName owner:uiView options:nil]; // 1 object, out of scope
for( id tempObject in bundle)
{
if( [tempObject isKindOfClass:[uiView class]] ) object = tempObject;
break;
}
return object;
}
As you can see in the following screen shot, the bundle has one object reference, but it's out of scope.
And debugging:
This is my code for instantiation:
subView = [[MyView alloc] initWithFrame:CGRectZero]; // ok
NSAssert(subView != nil, #"MyView was nil"); // fail
Any ideas on why the other S.O. poster was able to get it to work but this does not?
The use of the owner seems a bit confusing in the way that you are loading a nib. It appears that you are trying to use the view as both the owner of the nib and the first object in it.
Are you trying to load MyView from your nib (i.e. is the class of the view inside your nib files defined as MyView) or are you trying to load a subview of MyView from the nib?
If the view inside your nib is a MyView, here's how to load it. Create this static method as a category on UIView:
#implementation UIView (NibLoading)
+ (id)viewWithNibName:(NSString*)nibName
{
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName owner:nil options:nil];
if ([bundle count])
{
UIView *view = [bundle objectAtIndex:0];
if ([view isKindOfClass:self])
{
return view;
}
NSLog(#"The object in the nib %# is a %#, not a %#", nibName, [view class], self);
}
return nil;
}
#end
That will let you load any kind of view from a nib file (the view needs to be the first item defined in the nib). You would create your view like this:
MyView *view = [MyView viewWithNibName:#"MyView"];
If the view inside the nib is not a MyView, but you want to load it as a subview of MyView, with MyView defined as the file's owner in the nib file, do it like this:
#implementation UIView (NibLoading)
- (void)loadContentsFromNibName:(NSString*)nibName
{
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
if ([bundle count])
{
UIView *view = [bundle objectAtIndex:0];
if ([view isKindOfClass:[UIView class]])
{
//resize view to fit
view.frame = self.bounds;
//add as subview
[self addSubview:view];
}
NSLog(#"The object in the nib %# is a %#, not a UIView", nibName, [view class]);
}
}
#end
Using that approach, just create your view as normal using initWithFrame, then call loadContentsFromNibName to loa the contents from a nib. You would load your view like this:
MyView *view = [[MyView alloc] initWithFrame:CGRect(0,0,100,100)];
[view loadContentsFromNibName:#"MyView"];
Your [JVUIKitUtils initWithNibName:#"MyView" withOwner:self] approach is all wrong. When an init method is called, the object is already allocated by the call to [MyView alloc]. The reason why you chain the calls to the super init method is so that this will daisy chain all the way down to the NSObject init method, which simply returns the instance it is.
By setting "self" to the (autoreleased) instance value returned by your JVUIKitUtils, you are effectively setting it to a memory address other than what was allocated by the call to [MyView alloc], which generates the out of scope error.
Instead, don't create the init method your are trying to create. You already have the method to create and initialize your nib-based view in your state method. I would change the name and do something like:
+ (UIView)newViewWithNibName:(NSString*)nibName withClassType:(Class)uiView
{
id object = nil;
// you need some object to assign nib file owner to. use an NSObject.
NSObject* owner = [[NSObject alloc] init];
NSArray *bundle = [[NSBundle mainBundle] loadNibNamed:nibName owner:owner options:nil]; // 1 object, out of scope
for( id tempObject in bundle)
{
if( [tempObject isKindOfClass:] ) object = [tempObject retain];
break;
}
[owner release];
return object;
}
and then call it like:
MyView* subView = [JVUIKitUtils viewWithNibName:#"MyView" withClassType:[MyView class]];
I haven't tested this code. Your milage might vary.

question about init and load from xib for a custom UIView

I am trying to create a custom UIView class which loads from a xim file that contains the interface for that view. I am trying to encapsulate the [NSBundle mainBundle] loadNibNamed...] within the init method of my custom view as follows:
- (id)init
{
self = [super init];
if (self)
{
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"LoadingV" owner:self options:nil];
self = [(LoadingV*)[nibViews objectAtIndex: 0] retain];
}
return self;
}
I want to know:
Is this an acceptable way of doing so? And is there any better way?
Should i keep the "retain" given that i do not call [self release] in dealloc?
Cheers
AF
No, this is not acceptable, it's bad practice and you actually have a memory leak there.
Better way to do this is to use a pattern called "factory".
Example:
#interface CustomView: UIView
#end
#implementation CustomView
- (void)awakeFromNib {
// custom view loaded from nib
}
#end
#interface UIView (Nib)
+ (UIView *)viewFromNib:(NSString *)nibName bundle:(NSBundle *)bundle;
#end
#implementation UIView (Nib)
+ (UIView *)viewFromNib:(NSString *)nibName bundle:(NSBundle *)bundle {
if (!nibName || [nibName length] == 0) {
return nil;
}
UIView *view = nil;
if (!bundle) {
bundle = [NSBundle mainBundle];
}
// I assume, that there is only one root view in interface file
NSArray *loadedObjects = [bundle loadNibNamed:nibName owner:nil options:nil];
view = [loadedObjects lastObject];
return view;
}
#end
Usage:
// CustomView.xib contains one View object with its class set to "CustomView"
CustomView *myView = (CustomView *)[UIView viewFromNib:#"CustomView" bundle:nil];

iOS initWithNibName for UIView

Is it possible to implement initWithNubName for a custom class that extends UIView.
Example:
.h
#interface UIPullerView : UIView
{
}
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
.m
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
NSBundle *bundle = nibBundleOrNil==nil ? [NSBundle mainBundle] : nibBundleOrNil;
NSString *nibName = nibNameOrNil==nil ? #"UIPullerView" : nibNameOrNil;
if (self == [[bundle loadNibNamed:nibName owner:self options:nil] objectAtIndex:0])
{
NSLog(#"yes, the same class");
}
return self;
}
some controller class calls this
UIPullerView *puller = [[UIPullerView alloc] initWithNibName:nil bundle:nil];
This is not a working example because I don't set the first XIB object to self. If I would I would loos a reference to the main class right? So I would like to add a method that would read XIB file and replicate the object from XIB into self. I don't want to use UIViewController but the UIView so no additional views should be added to main view.
Se also how I set the XIB:
I would do
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
...
self = [[bundle loadNibNamed:nibName owner:self options:nil] objectAtIndex:0]
return self;
}
but this isn't right true?
Is that possible?
To get around the issue of assigning self and losing the main class reference, I wonder if you should do it similarly as with loading a UITableViewCell from a NIB:
[[NSBundle mainBundle] loadNibNamed:#"MyTableCell" owner:self options:nil];
cell = myTableCell;
self.myTableCell = nil;
and set the class that is doing this as the owner of the NIB.
Of course, if you're instantiating your UIPullerView in multiple, different, places then that last part gets tricky...
Create a ExampleView.xib
Create ExampleView.h and ExampleView.m
#interface ExampleView : UIView
#end
In the ExampleView.xib's Identity Inspector, set Custom Class to ExampleView
In Example.m, add awakeFromNib
- (void)awakeFromNib
{
[super awakeFromNib];
// [self whateverInits];
}
which will be executed whenever the view is loaded, say, by:
NSArray *objects = [[NSBundle mainBundle] loadNibNamed:#"ExampleView" owner:self options:nil];
ExampleView *exampleView = [objects firstObject];
Try this:
- (instancetype)initWithNib {
NSString *nibName = [NSString stringWithUTF8String:object_getClassName(self)];
return [[[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil] objectAtIndex:0];
}

Is it correct to initialise a UITableViewCell using initWithCoder?

I am loading a UITableViewCell using,
[[NSBundle mainBundle] loadNibNamed:#"BlogCell" owner:self options:nil];
cell = blogCell;
self.blogCell = nil;
where blogCell is an outlet to BlogCell.xib
The actual UITableViewCell is of type BlogCell which is a subclass of UITableViewCell.
In my BlogCell class I am using
-(id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
//initialisation of some cell properties here
return self;
}
Is this correct? Is this the way you would tackle initialisation of a cell loaded from a nib?
Thanks
That, or in awakeFromNib.