reusable ui components using nibs - ios

I like using interface builder to design ui elements but am designing a screen in which different subviews display depending on conditions. For me its easier to design those subviews in seperate nib files and then load them into the view controller when needed.
e.g. I have the following activity view that is lazy loaded that is basically an overlay that displays an activity indicator, I would like the option to reuse this in different view controllers.
Here is the getter method in one of the view controllers
- (ActivityView *)activityView
{
if (!activityView) {
activityView = [[[UINib nibWithNibName:#"ActivityView" bundle:nil]
instantiateWithOwner:self options:nil] lastObject];
}
return activityView;
}
In the nib file I do not set the owner to match the view controller as I would like multiple view controllers to be able to use it, but I link the top level view to an ActivityView class so I can customize some of its ui programatically.
This approach seems to work for me but I wonder if this is a good approach?
In the resource programing guide it says:
Important: You are responsible for releasing the top-level objects of any nib files you load when you are finished with those objects. Failure to do so is a cause of memory leaks in many applications. After releasing the top-level objects, it is a good idea to clear any outlets pointing to objects in the nib file by setting them to nil. You should clear outlets associated with all of the nib file’s objects, not just the top-level objects.
I was not sure if this was still applicable if I have ARC and what I would need to do about it?
Does this just mean I need to set self.activityView = nil in the didReceiveMemoryWarning method?

Related

Are nib/xib files meant to be used in diffrent view controllers ?

I watched multiple videos on youtube and bought two books with explanations (O'Reilly).
It seems like a nib file is always used in one view controller.
The file-owner is usually the view controller which will implement that nib.
I couldn't find an example in which a nib file is used in multiple view controllers.
I did come across a couple of posts that found ways around this but the posts are old and many people leave warnings.
My initial urge to use a nib file was because I had a table view cell that was used in multiple tableViewControllers. The tableViewCell was prototyped in one of the TableViewControllers in storyboard.
I thought that creating a nib would decouple that cell from one specific TableViewController.
I can't say that I really understand the purpose of a nib file.
It seems like it's very similar to storyboard.
I ended up creating the tableViewCell in code and reusing it throughout my project.
So my question is what are nib files used for ?
Are they used to decouple a view from a specific view controller ?
Are they used to avoid duplication within one ViewController ?
Any insight would be helpful.
Yes, you can reuse xibs in many places within your app.
Xibs predate storyboards and act as factories for creating instances of UIView classes configured in Interface Builder. Using them to define the layout and attributes of a UIViewController's view and its subviews is one common use case but certainly not the only one.
A common place where a xib might be reused is when it contains a table or collection view cell. You could then register that xib with multiple table or collection views using register(_ nib: UINib?,
forCellWithReuseIdentifier identifier: String) to load instances of the cell when needed.

What is the correct way to share same code between several viewControllers?

I have several view controllers (or to be specific, tableview controllers), and all these view controllers use the same view (a loading view) that can be displayed while data access is running.
Currently, I have the same code in all these view controllers:
// Loading the loading view
NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:#"loadingView" owner:nil options:nil];
loadingView = [nibContents lastObject];
[self.view addSubview:loadingView];
I also use a toast view for that displays success or errors after operation within the controller.
My question is, what is the proper way to avoid code copy/pasting?
I have tried categories :
Doesn't seem to work since I can't store the reference to the loading view.
I also have tried inheritance, but without much success (the child controller doesn't recognise the properties (loading view) of the parent view controller).
Could you lead me to the right way to do this?
I'm using a storyboard and NIB files for the specific views (loading and toasts).
Thanks in advance.
I have tried categories : Doesn't seem to work since I can't store the reference to the loading view.
View controller categories, or alternatively class methods on the views you are displaying, are the best way to deal with this problem.
You can use associated objects to keep a reference to the view that is being displayed. If you're wary of those, a static variable can be used to give persistent storage without requiring a property or instance variable. These both assume you will only be showing one loading view or alert view at once, which is to be expected.
For categories you'd define a method like
-(void)setLoading:(BOOL)loading
On a view controller. This would have a static variable which referred to the loading view. If you were passing YES, you'd create the view, show it and assign it to the static (if it wasn't already present). If you were passing NO, you'd remove the view and set the static back to nil.

Using multiple nib files with a single view controller?

Background
I'm using interface builder to create the UI for an app I'm working on. The app has a single screen that displays a series of buttons. Clicking on a button displays an associated view which overlays the buttons. Clicking another button hides the previous overlay view and displays another one.
Too make managing the UI easier in IB I've decided to create multiple nib files for each sub view that is to appear when clicking the relevant button. I'm then loading the sub view's nib file in the view controller's viewDidLoad method using the UINib class.
The idea behind this was to avoid having multiple views stacked on top of each other in a single nib file as this would be hard to manipulate in IB. I could have created all the views in code but this would require a lot of tedious coding as the layouts of each sub view are quite complex (with multiple child views).
Example code loading a sub view from a nib file.
- (void)viewDidLoad
{
UINib *aSubViewNib = [UINib nibWithNibName:#"aSubView" bundle:nil];
NSArray *bundleObjects = [aSubViewNib instantiateWithOwner:self options:nil];
// get root view from bundle array
UIView *aSubView = [bundleObjects objectAtIndex:0];
[self.view addSubview:aSubView];
...
The code above is repeated for the other views.
To summarise I have a single screen iPhone app that has layered views that are shown/hidden by clicking buttons. This is achieved with a single view controller with an associated nib file and a series of additional nib files for the sub views which are loaded in the view controller's viewDidLoad method.
Questions!
Sorry for the long introduction but I wanted to be very clear what it is I am doing.
Is my approach bad or unusual?
Are there any potential issues to doing it this way?
What have other people done when they need a dynamic interface and
still want to keep everything in Interface Builder?
Notes
Before anyone asks why don't I just display the sub views on a new screen and use the navigation bar, let me say that I have very good reasons and I do understand iOS UI guidelines. The above use case is not exactly my use case but it's one that clearly describes the problem without getting bogged down in my development app.
Also I know I could have written all the sub views as code but each sub view has a complex layout of child views and it would be a lot of code and messing around to try and get them looking right.
Thanks in advance.
There isn't necessarily a 1-to-1 relationship between view controllers and views. Most views contain many subviews, which are views themselves, so this literally doesn't make sense.
However, depending on the complexity of the views (including their content), you may want separate view controllers... or not.
For example, if you have two sbuviews that are each tableViews, you may want to have one view controller for each tableView. This is because each tableView is looking at the same delegate methods, and if they are in the same viewController, then the delegate methods have to differentiate between the tableViews. The delegate methods have signatures that allow this, but, in my experience, it can really make for a messy code design that is hard to follow and hard to manage.
On the other hand, you may have two tables that are managed by the same viewController, where one table is filled with meaningful data and the other is simply a place holder (as when the data source is empty). One might be visible while the other is not. Why make you life complicated by creating two view controllers when both are driven by the same data source (the model)?
In my mind, it comes down to how difficult it is to follow and manage the code. If the complexity of using a single view controller becomes burdensome, consider using more view controllers.
UPDATE
By the way, I have an example that I am currently working with that may illustrate a similar situation. In the InAppSettingsKit, that a lot of developers use, there are several xib files for pieces of the main view. You can look at the structure here on github. There is one main view controllers and several xib files. (There is also what I would call a "helper" view controller and an email composer view controller.) In this example, the xib files may be used multiple times to specify the layout of table view cells. There is no view controller for each xib file, though. (The documentation for InAppSettingsKit is sparse, so these things may not be obvious just by taking a quick look at it.)
Every View should have a corresponding UIViewController. Using one ViewController to "Control" more than one view breaks the MVC paradigm. "Controlling" multiple "views" from one controller will make it much harder to change one thing without breaking something else. The choices you make on how to present the content to the end user will be different for every individual. So if you say a NavigationController won't work in your case, maybe a Modal view is the answer or, you might just instantiate your custom UIViewControllers and add them to your view ([addSubview:]), if thats the road you want, but like I said, it would be beneficial for you to make a "controller" for each view object along with the corresponding xib. If you need information sent back, use a delegate or use Notifications to send the message back to the parent view. I learned the hard way that not following MVC paradigm, will make you life miserable. Try and keep your code as decoupled as possible. And read up on the MVC design pattern, you won't regret it.
actually its possible to do this.
Open your .xib file,select File’s Owner(in placeholder) -> "identity inspector" (utilities) -> change class name to your controller classname -> press control and drag file's owner placeholder to View object, select "view" in dialog.
Now you can customize your view.
p.s. you can use the same outlets as first xib, you need only to drag them to the new xib(+control sure).
here is an explained tutorial:
http://irawd.wordpress.com/2013/09/05/how-to-link-a-xib-file-to-a-class-and-use-2-xib-files-for-iphone4-and-iphone5/

Does XIB always need a view controller?

If I do an interface in IB must I always need a base UIViewController or can I just skip straight to a UIView ?
As of now I'm doing all my design in obj-c which makes for a bit more busy work.
If I have to use a UIViewController is there anyway to suck the UIView out of it if that's all I want?
I just want to be able to pull out static layouts from XIB instead of putting them together in obj-c.
Any suggestions are appreciated!
UIViewController has that handy initWithNib: bundle: method that makes everything so easy, but there are also ways to get objects from xibs via:
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:#"WhateverItsNamed" owner:self options:nil];
(and the nib array here contains objects which correspond to whatever objects are stored in the XIB file).
No, you don't need a UIViewController if it doesn't serve your needs.
After you have initialized a UINib, use its:
-instantiateWithOwner:options:
to access the nib contents. Make the nib's root object be a UIView (or subclass thereof). If you plan on making connections to File's Owner you will need to set File's Owner to a context-appropriate class.
No, you don't need to associate a UIViewController with a XIB.
First, do not add a view controller to the XIB. Then, you'd often use NSNib or UINib APIs to access the top level objects of the nib -- so you can avoid a view controller and set up any structure you'd like in the NIB editor, and programmatically access the objects in the NIB as needed.
In iOS 5, XIB are not recommended to be used anymore, you should use a storyboard instead.
And to simply answer your question, every top level view requires a view controller to be responsible for it.
All sub views can be an outlet in your view controller and be handled from there.

Custom Sized Nib File Controllers and Interface Builder

I've recently been introduced to development for iOS by a friend and have begun to experiment with the interface builder and view controllers. One thing I'm finding is that when using a nib in conjunction with a view controller, your view controllers ivars are quickly polluted with views you may never actually reference. I would like to modularize the key components in my main nib into several different views. I have two questions regarding this:
How can I create a nib file for a custom sized view (one that doesn't fill the entire screen)?
How can I add the newly modularized to my main nib (all of the classes I would create for the components would be view controllers not views)?
Assuming I were to alloc and init the view controllers and add them to the main view programmatically, how could I position the custom sized views since I have already called initWithNibName:bundle:. I can't call initWithFrame: right?
Answers would be much appreciated!
Thanks,
PhpMyCoder
EDIT
I've discovered the answer to my first question. It seems that in the attributes inspector of your nib file you must disable the status bar (change it to unspecified) to enable the editing of the height and width parameters in the size inspector. However, I still am unsure of how to add these custom nibs and their view controllers to another nib without coding them in with initWithFrame: and addSubview:. Any ideas on adding a view controller's view to a nib in IB?
EDIT 2
Added question 3 (or 2 depending on how you think about it).
EDIT 3
I seem to be hastily asking questions today. A simple call to setFrame: will deal with sizing and positioning (and you can even append it on to your init function initWithNibName:bundle:frame:). Still not sure how to add the view (created by a nib) from a view controller to another nib in Interface Builder. If this is possible, I'd love to hear how.
Remember to not get ViewControllers and Views confused.
They are both in a hierarchy, and each ViewController has/controls a "main" view (which likely has bunches of subviews).
You don't add a view to a nib. A nib is a mechanism to help you assemble views. (A NeXT Interface Builder file, if we delve into nomenclature.)
This is how you load a nib:
[[NSBundle mainBundle] loadNibNamed:#"foo" owner:self options:nil];
Normally, you give a nib name to a controller and it does it for you, but you can do this to. There are complex and tricky ways to access the content. The following is the standard way to do it.
The owner you pass in must be the type declared as the owner in that nib file. It should have some of its outlets connected to objects in the nib file. After you load the nib file, they'll just "be there". If you called this twice, it would overwrite the first ones and replace them with the second ones. (mostly harmless, definitely useless)
So, typically, you wired it up to view. Now you have a view that's floating around in memory and not connected to the view hierarchy of the application. Time to do that. You must take view and figure out where it belongs in the pre-existing hierarchy and call [someOtherView addSubview:self.view] no it, and it will appear. Yes, if you want to explicitly place/size it, you will need to do that. Note that view.frame is in the superview's coordinate system.

Resources