I've been struggling with this for a while and I think the solution is really simple, but I just can't get it right. I have a UIViewController, which has its view and now I would like to add a subview to it. Subview should be loaded from a nib. I've followed the steps described here, ie.:
1. Create MyView class which is a subclass of UIView
2. Declare IBOutlet properties in MyView
3. Make .xib file, where File Owner is set to UIViewController and View class set to MyView
4. Connect outlets
5. In MyViewController, viewDidLoad method :
NSArray *nibViews = [[NSBundle mainBundle] loadNibNamed:#"MyView" owner:self options:nil];
MyView *mView;
for (id view in nibViews) {
if ([view isKindOfClass:[MyView class]]) {
mView = (MyView*) view;
}
}
[self.view addSubview:mView];
mView is loaded successfully, but when I try to add it as a subview I get EXC_BAD_ACCESS. I've even added: mView = [(MyView*) view retain]; but that doesn't help.
What am I doing wrong?
Don't worry...
You don't need to take separate nib file and referencing to the myView class again. Simply you can drag UIView from library into your current viewController's xib, and then u can simply connect the view from current view to the xib.
See below images:
i'm not sure of your point 3:
Make .xib file, where File Owner is set to UIViewController and View class set to MyView
the file owner shouldn't be the uiviewcontroller, but the MyView class
In my case, I didn't want my view controller to have any knowledge of the IBOutlets from my view's .xib. I wanted my view subclass to own the IBOutlets. Unfortunately UIView doesn't have an initWithNibName: method, so I just created my own category.
Here's what I did:
In IB, click on your main UIView, and in the Identity Inspector, set the class to your subclass
In IB, click on File's Owner, and in the Identity Inspector, set the class to your subclass
Use your new category method initWithNibName: to instantiate your view.
And here's the category I created:
- (instancetype)initWithNibName:(NSString *)nibName
{
NSArray *arrayOfViews = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:nil];
if (arrayOfViews.count < 1) {
return nil;
}
self = arrayOfViews[0];
return self;
}
Inspired by this post.
Note though, that so far the frame will adjust automatically, so unlike the code in the post, I haven't yet had to explicitly set the frame.
Also, unlike the post's code, I needed to set owner:self so the IBOutlets would be wired up correctly.
Related
I am trying to create a custom Picker view ,connected the touchupinside event to my PickerViewViewController.h class as funcion
- (IBAction)Done:(id)sender;
added funcion in .m
- (IBAction)Done:(id)sender {
NSLog(#"Done");
}
Added the view to my main view as
PickerViewViewController *pic=[self.storyboard instantiateViewControllerWithIdentifier:#"Pickerview"];
[self.view addSubview:pic.view];
but when i click done button it crashes ,i had done it before using XIB easily ,help?
First of all try renaming your function something like
- (IBAction)doneButtonPressed:(id)sender
Done might be reserved by the system for something (not sure, but it happened to me before)
You could just drop the storyboard ... and make a normal ViewController with a .xib :). Actually there is no need of a ViewController. Just make a UIView and a .xib linked to your UIView.
To attach a .xib to a UIView you can use:
+ (CustomViewName *)getNewCustomView {
NSArray *xib = [[NSBundle mainBundle] loadNibNamed:#"CustomViewName" owner:nil options:nil];
for (NSObject *obj in xib) {
if ([obj isKindOfClass:[CustomViewName class]]) {
return (CustomViewName *)obj;
}
}
return nil;
}
and intialise it like :
CustomViewName *customView = [CustomViewName getNewCustomView];
customView.frame = CGRectMake(...);
self.view addSubview:customView;
also in your .xib file you must set the name of your class ... but on the VIew not on the file's owner like so:
I am in the process of developing an IOS rpg. This game is a controlled by a tab bar, and every view controller in the tab bar will have a common "header" that sits at the top of the screen and shows information about the player.
The rest of the screen, however, will show one of many different views. Each view controller will be responsible for showing multiple different "views" underneath the "header" view. In addition, many of these views will need to be scrollable, as they will not fit in the confines of the screen.
Questions:
1)How do you add two views from separate nibs to a single view controller.
2)How do you embed only one of those views in a scroll view.
Thank you.
You can load a nib through the loadNibNamed:owner:options: function on a NSBundle. What it will return is an array of all the objects in the nib (the list you see on the left when you create a nib in interface builder). If you're view is the first item on the list of objects in the nib, then its the object at the 0th index of that array.
NSArray *objects1 = [[NSBundle mainBundle] loadNibNamed:#"View1Nib" owner:self options:nil];
UIView *customView1 = [objects1 objectAtIndex:0];
NSArray *objects2 = [[NSBundle mainBundle] loadNibNamed:#"View2Nib" owner:self options:nil];
UIView *customView2 = [objects2 objectAtIndex:0];
UIScrollView *scroll = [[[UIScrollView alloc] init] autorelease];
[scroll addSubview:customView2];
[[self view] addSubview:customView1];
[[self view] addSubview:scroll];
If I have multiple views in a nib I make use of the restoration identifiers rather than relying on the order of the array and perform the following:
UINib *nib = [UINib nibWithNibName:#"Nib" bundle:nil];
NSArray* views = [nib instantiateWithOwner:self options:nil];
assert(views.count == 3);
UIView *aView;
UIView *anotherView;
UIView *yetAnotherView;
for (UIView* view in views) {
if ([view.restorationIdentifier isEqualToString:#"AViewId"]) {
aView = (SettingsCell *) view;
}
else if([view.restorationIdentifier isEqualToString:#"AnotherViewId"]) {
anotherView = (SettingsCell *) view;
}
else if([view.restorationIdentifier isEqualToString:#"YetAnotherViewId"]) {
yetAnotherView = (HeaderView *)view;
}
}
assert(aView && anotherView && yetAnotherView);
When you make a view controller, if you choose to generate an xib automatically, its view outlet will, by default be connected to a view. Now, create a new xib, with some different name, and make its files owner as your view controller class. Also, manually connect the view outlet.
Now, call the init method:
YourViewController *x = [[YourViewController alloc] initWithNibName: #"yourNibName" bundle:nil];
according to whatever xib you want to load, place the name instead of "yourNibName". Hope that helps.
You'll need to have references to both views, and you can simply [view addSubview:secondView]; as normal. As for how you get a reference to the views in the xib that is not associated with your view controller, there are several ways, but which you choose will depend on whether that view is already instantiated elsewhere in the app. I'm betting you're already instantiating that view elsewhere, and you simply want to add it. If it were me, I would use a singleton for that view's parent, so I could do something like:
[view addSubview:[ParentClass parentClassSharedInstance] viewToAdd]];
Scroll views are a beast you'll need to work with to fully understand, but you add views to them just like any other view. The important bit is that they have a contentSize property that can be bigger than their frame's size. I usually use a single view of the size I want to manage all views underneath the ScrollView. Good luck!
I would like to create a main view controller in order to handle a custom navigation bar and its behavior.
Then, I would like to subclass this new class to use its behavior and link a "contentView" IBOutlet for each of my ViewControllers.
Here is my BaseViewController.xib:
TestViewController.xib: linking the "contentView" outlet for its parent
TestViewController.h: inherit from BaseViewController
#import "BaseViewController.h"
#interface TestViewController : BaseViewController
#end
TestViewController.m: should use outlets from TestViewController.xib
- (id)init
{
self = [super init];
if (self) {
[[NSBundle mainBundle] loadNibNamed:#"TestViewController"
owner:self
options:nil];
}
return self;
}
Obviously, I would like to have the green bar from MainViewController with the white view and the label from TestViewController, but it's actually not working, here is the result:
If in TestViewController.xib, I link the "view" outlet with a view, it's actually overriding this screen and I don't have the green bar from MainViewController, so I guess that my import is working, but I can't understand why it's not working with the contentView.
Any idea ?
Cheers!
Cyril
You will have to decide which controller should control the view with the content.
Either you have the base view controller control it - in which case you do not need to subclass it. Just add a subclass of UIView as a subview and do all the logic there. The view can also have its own nib file.
Or you subclass your base view controller - but then you cannot have a separate view controller nib. You can do all the view controller logic (data sources etc) in the subclass (which is also a view controller), but it will use the nib of the base view controller.
I know how to place a nib file in the "main" view controller:
when I execute this method a new nib file will be displayed.
Is it possible to place that nib file in a UIView controller instead?
I want to place that nib file in that view controller instead of in the main view. How could I do that?
Edit:
I tried doing what you mention Paul I don't know what am I doing wrong. Anyways here is what I did:
I created another view controller with the properties an methods you mentioned.
I hooked up my IBoutlet UIView *exampleView to the base view:
then from here I am lost I don't know where to place the other methods. I would like to place that exampleView into:
that view.
I have tried placing them in pgBackground.m but it does not work. I want to place them inside an IBaction so that I can load that with a touch up inside event with a button.
I managed to place a nib file in a uiview control but I have the problem that the subview will not rotate if the device does. I am currently working on a solution to this but so far here is a question that can show you how to add the view from another nib file to a uiview control.
If I follow I think you will need to:
Create a UIView sub class with associated xib
ExampleView.h
ExampleView.m
ExampleView.xib
Set up a property to contain the View hierarchy (everything you want to be hooked up in Interface builder)
ExampleView.h
#property (nonatomic, retain) IBOutlet UIView *exampleView;
ExampleView.m
#synthesize exampleView = _exampleView;
In the ExampleView.m you need to add:
- (void)awakeFromNib
{
[super awakeFromNib];
[[NSBundle mainBundle] loadNibNamed:#"ExampleView" owner:self options:nil];
[self addSubview:self.exampleView];
}
You use awake from nib so you can use this sub class in Interface Builder. If you also want to instantiate it in code you need to hook up the xib in the init method. In this case I would probably extract the nib loading like so:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self loadNibIntoView];
}
return self;
}
- (void)awakeFromNib
{
[super awakeFromNib];
[self loadNibIntoView];
}
- (void)loadNibIntoView
{
[[NSBundle mainBundle] loadNibNamed:#"ExampleView" owner:self options:nil];
[self addSubview:self.exampleView];
}
In the ExampleView.xib file make sure to hook up your IBOutlet UIView *exampleView to the base view.
Now in any xib file where you want to use your custom view simply drag it into your window and change the class to your subclass. This is how I got it working I look forward to some people suggesting improvements.
can u tell me what u want to do? if u want to change orientation then first u declare a bool en type variable in app delegate file then define in app delegate.m file if orientation change then detect and make Boolean type variable true for landscape and false for portrait. this process use for next all views. and u want u execute nib in main view controller then u have two ways one is dynamically and other by interface builder. in interface builder u Select navigation controller and drop in main window file . after that u change the view of navigation controller's view by press cmd+4 and select view which u want to display first.
-(IBAction)btnSubmit_click{
UIDeviceOrientation orientation = [[UIDevice currentDevice]orientation];
if (orientation == UIDeviceOrientationLandscapeLeft) {
// Do stuff
}
else if (orientation == UIDeviceOrientationLandscapeRight) {
// Do stuff like [self setCordinateLandscape];
}
}
-(BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation) interfaceOrientation
{
// Return YES for supported orientations
//set as per requirements ====>>
return (interfaceOrientation == UIDeviceOrientationLandscapeRight);
// Or return TRUE;
}
I have a nib file I'm trying to instantiate in code. My UIViewController's main view is also loaded from a nib file.
Here's my UIViewController's viewDidLoad method:
- (void)viewDidLoad
{
[super viewDidLoad];
UINib *nib = [UINib nibWithNibName:#"MyCustomView" bundle:nil];
NSArray *nibViews = [nib instantiateWithOwner:self options:nil];
MyCustomView *myView = [nibViews objectAtIndex:0];
myView.frame = CGRectMake(100.0f, 100.0f, 91.0f, 91.0f);
[self.view addSubview:myView];
}
This creates some sort of endless loop. If I comment out [self.view addSubview:myView], myView appears, but everything currently on the screen disappears. I didn't think instantiateWithOwner added the view to the screen. If it does, how do I get access to it?
Thanks for your help.
The instantiateWithOwner method reassigns the properties of your view controller (set from the nib the controller was created from) to ones from the new nib. Those properties likely include the view property, so that method, within it, contains a call to setView:, and sets the view controller's view to the new nib's view. Afterwards, you're trying to add a view as a subview to itself, and that, naturally, causes problems.
You want to create your own property, for instance, secondaryView, set the nib's view to that, and add it as a subview. You don't want to reassign your view controller's view.