I have been using UIViewControllers and initWithNibName with much success, basically using them as a convenient way to design the view with Interface Builder. Unfortunately I have built a hierarchy of views before noticing this line in the UIViewController documentation:
Note: You should not use view
controllers to manage views that fill
only a part of their window
My question is this: Having a very simple NIB that only has a UIView in addition to the default First Responder and Owning Object, what is the simplest way to load the UIView into my code?
I have not been able to get loadNibNamed:owner:options: to work at this point, but suspect the answer will involve it somehow.
Yes, just call
[[NSBundle mainBundle] loadNibNamed:#"viewNib" owner:self options:nil];
You normally do this from the view controller you have set as File's Owner in the NIB. That way, you can declare an outlet for the view in the view controller which will automatically get connected when you load the NIB file. You don't even have to work with the return value of the method in this case.
Related
I am having some problems and wondering if someone could help.
I have always used storyboards so I am rusty at programmatically creating UIViews.
Basically, I am loading data from an API and displaying it in a UITableView. The problem is, on one part of my app, the loading of the data takes at least 10 seconds as it loads 10 different things from the API. (This must use separate calls). I use a sleep of 1 second so as not to make too many calls to the API per second (Hence why it takes so long to load). This is integral to my app.
I have implemented a count which increments after each call. Now all I am hoping to do, is create a UIView with a label and progress bar to say that it is loading and how much. This must be displayed over the tableview.
How do I go about doing this?
I do it this way:
1 - Create a UIView subclass, with outlets to all of the view's sub elements.
2 - Create a xib file. Change the class of the top view to the name of your UIView subclass. Add all the elements you want to the view and then hook up the outlets from the topmost view to the elements. File's Owner remains blank and unused.
3 - In the view controller that I want to use the view in I add:
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"MyCustomView"
owner:self
options:nil];
MyCustomView *myCustomView = [nibContents objectAtIndex:0];
Then you can add the custom view to your table view. I would probably add a property in the view controller and assign the custom view to it so I could access the custom view using self.myCustomView.
I have a custom view I made with an IB file since it's quite complex: (RotatorView.xib/.h/.m).
I want to add the RotatorView as a subview to a view controller. I can do this programmatically using:
NSArray *array = [[NSBundle mainBundle] loadNibNamed:#"RotatorView"
owner:self options:nil];
But I'd rather not do that. It would be better for my design if I could add the RotatorView to the canvas of a View Controller in Interface Builder. That way I could use IB to manage the properties of each instance of RotatorView instead of setting properties programmatically in each class. Is there a way that I can add my custom view with XIB to a parent view controller? Do I have to build an IB plugin to do this?
You can add a UIView control to the canvas in IB and change its class name to match your custom view class name.
Although this will save you from calling loadNibNamed method programmatically, but you'll not be able to set the custom properties directly from IB.
The only solution for doing this is to build an IB plugin, but unfortunately it seems you can't make Cocoa Touch IB plugins because you can't make Cocoa Touch frameworks.
In my iPhone app, I made a custom UIView that I use in several different view controllers to display some information.
I'm familiar with making customer table cells for use in a table but I've never made a custom view again.
My question is what is the best way to load this view? I've tried simply placing the view in the view controllers I want it to appear in using IB, but that doesn't seem to be enough. It seems to me that it would make sense that if you set the Class Identity for the view that it should load up that view when the view controller is created. Or maybe it doesn't because it doesn't know what nib to get it from (it's in its own nib, of course).
Since it's not a view controller, I can't just use initWithNibName. I've tried using NSBundle loadNibNamed like I do with table cells but it seems like an awfully large amount of work. The code looks like this:
NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:#"MyCustomView" owner:nil options:nil];
for (id currentObject in topLevelObjects) {
if ([currentObject isKindOfClass:[MyCustomView class]]) {
self.customView = currentObject;
break;
}
}
[self.view addSubview:self.customView];
There must be an easier way which doesn't involve the loop. Also, creating the view this way makes me lose all the info from IB on where I actually placed the view. It's created at 0,0 which is not where I want it. Yes, I know I can set a new frame, but again, there must be an easier way. Is there a way I can do this all through IB? Or at least with less work programmatically?
I also make it that way. I ran into the problem that I want to subclass UIView and using nib at the same time. I don't think there's better way to do so, because nib file can contains many objects and you have to specify which one to use. However, do it this way you can put IBOutlet in your MyCustomView and bind it in IB so the view can manage itself e.g. overriding layoutSubviews.
Try this: create a new UIViewController subclass (called ParentViewController for the sake of answer) that each of your controllers that need to display this information will then also subclass. This controller should not have a nib associated with it.
In ParentViewController, create an IBOutlet for a UIView that will hold a reference to a view containing your custom nib. (Called customView, again just because)
Now, open your nib (for the commonly used customView) in Interface Builder and select the File's Owner. Now in the Identity tab, choose the ParentViewController created above for the class. Now here comes the trick. In Connections, remove any connection to "view" (if there is any) and create a connection from the customView outlet to the topmost UIView of the nib that defines your customView.
Now, when you do a loadNibNamed in the code for a subclass of ParentViewController, it should automagically load the UIView from your nib and put a reference to it in the outlet for you to use. No loop required.
You could also add outlets for any other interface components in your nib and do the same thing.
Good luck!
I have a VC controlling a screen of content that has 2 modes; a normal mode and an edit mode.
Can I create a single VC with 2 views, each from separate nibs?
In many situations on the iphone, you have a VC which controls an associated view. Then on
a button press or other event, a new VC is loaded and its view becomes the top level view etc.
But in this situation, I have 2 modes that I want to use the same
VC for, because they are closely related. So I want a VC which can swap in/out 2 views.
As per here:
How to load a UIView using a nib file created with Interface Builder
I have found that I can load a VC with an associated view from a nib and then later on load
a different view from another nib and make that new view the active view.
NSArray *nibObjects = [[NSBundle mainBundle] loadNibNamed:#"EditMode" owner:self options:nil];
UIView *theEditView = [nibObjects objectAtIndex:0];
self.editView = theEditView;
[self.view addSubview:theEditView];
The secondary nib has outlets wired up to the VC like the primary nib. When the new nib is loaded,
the outlets are all connected up fine and everything works nicely. Unfortunately when this edit
view is then removed, there doesn't seem to be any elegant way of getting the outlets hooked up
again to the (normal mode) view from the original nib. Nib loading and outlet setting seems a
once only thing.
So, if you want to have a VC that swaps in/out 2 views without creating a new VC, what are the options?
1) You can do everything in code, but I want to use nibs because it makes creating the UI simpler.
2) You have 1 nib for your VC and just hide/show elements using the hidden property of UIView and its subclasses.
3) You load a new nib as described above. This is fine for the new nib, but how do you sort the outlets when you go back to the original nib.
4) Give up and accept having a 1:1 between VCs and nibs. There is a nib for normal mode, a nib for edit mode and each mode has a VC that subclasses a common superclass.
In the end, I went with 4) and it works, but requires a fair amount of extra work, because I have a model class that I instantiate in normal mode and then have to pass to the edit mode VC because both modes need access to the model. I'm also using NSTimer and have to start and stop the timer in each mode. It is because of all this shared functionality that I wanted a single VC with 2 nibs in the first place.
I would just add another view to your original nib, and wire it up to another IBOutlet called 'editView'.
#interface TestViewController : UIViewController {
IBOutlet UIView *editView;
}
#end
Then you can can do a [self.view addSubview:theEditView]; whenever you need to show it.
As a newbie, IB and all the possible connections is bewildering to me. Most tutorials I've found are what I'd call the reincarnation of spaghetti code, in which the entanglement is all the connections created by dragging. Of course, I want to use IB for layout of views (sizing & placing visual elements), that's what IB is great for. But a controller is not a view, so it's less confusing if all my controllers are solely code and don't appear anywhere in IB. I suspect this will minimize the spaghetti. It also encourages the one-xib-per-view admonishment. To that end, and here's the question, where can I find example projects that adhere to this strategy?
I don't have a set of sample projects, but I will give you some information about how things work and when you should create controller's in a XIB file or in code.
If your controllers are created dynamically by way of a user's action, you typically won't instantiate them in a XIB file. Instead, you'll instantiate them in code like harms mentions above. Once you do that, you'll still need a mechanism to connect this controller that was created in code, to the user interface elements that you've created in IB.
The mechanism that IB provides for solving this is the File's Owner. Mastering the File's Owner is essential to "getting" Interface Builder.
The file's owner is not an object that is "in" the XIB file, it's an object that's represented in the XIB file. It's a placeholder for an object that will already exist when the XIB file is loaded. When NIB files are loaded at run time, they're loaded with the NSBundle method -[NSBundle loadNibNamed:ownwer:options:]. The owner parameter is used to resolve the file's owner placeholder object in the XIB/NIB file. When the file is loaded at runtime, all of the connections made against the file's owner will resolve against the object you passed in as the owner parameter to the NSBundle method. On the iPhone, you typically don't load NIB file yourself. Instead UIViewController does it for you. The default implementation of UIViewController's loadView method might look something like this:
- (void)loadView {
[[self nibBundle] loadNibNamed:[self nibName] owner:self options:nil];
}
So, by connecting the elements in your XIB file to the file's owner, you'll be connecting them to your view controller.
You will have some controllers that are statically in your application - they'll be alive forever. A navigation or tab controller along with their root items are typically alive for for the entire life of their application. When that's the case, I'd set those view controllers up in the MainWindow.xib file. Most of the other controllers would be created dynamically, and programatically in response the the user doing things.
Good question. The pattern I try to stick with is making the controller in code, adding IBOutlets and IBActions for the things in the XIB/NIB that interact with that code, and a thing in the controller's constructor which loads the XIB/NIB with "self" as the owner, and in IB I connect stuff to the "File's Owner" placeholder, whose identity I will have specified to be the my controller's class.