Custom Sized Nib File Controllers and Interface Builder - ios

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.

Related

Reusing view on different scenes in IB or programatically

I've a view which I want to reuse on other scenes in IB. This view contains user details such as name, avatar, description, few buttons etc. These views are exactly same and have same elements inside them. Right now it's pain to copy them across scenes, fix the constraints and then code the same elements over and over again. It's not quite productive and it's time consuming. Ideally I want to see these views on IB so I don't want to code do everything programmatically without any visual. Is there a way we can make it in better way so that I can just reuse it?
Not only is it pain, it is poor design to rebuild the same component. I would create a xib file, create the backing swift or obj-c files. Wire up all the outlets as you would normally.
In each view controller or view that you need it, initialize it and add it as a subview.
In swift, I would do something like:
if let subview = Bundle.main.loadNibNamded("SpecialView", owner: self, options: nil)?.first as SpecialView {
// set constraints
containerView.addSubView(subview)
}
Yes you can. I've done this recently. I started in a VC that was a child of a NavigationController and pushed a view onto the view stack that was outside of the NavigationController's scope so the new view did not have the NavigationBar up top.
Assuming you are using a storyboard, you'll create the view with all of it's actions and outlets and link them up to its own class file. Also give the storyboarded view an ID. Then you need to define the controller as a constant and push it to your view stack when you want it used.
// define the controller
let controller = self.storyboard!.instantiateViewController(withIdentifier: "YourCustomControllerID") as! YourCustomController
// if you want to pass data to the new controller
// first you must define some class variables in that class then you do this:
controller.image = whateverImage
// then you push it to the stack like this
self.navigationController!.pushViewController(controller, animated: true)
To dismiss that view, you call dismiss(animated: true, completion: nil) in YourCustomController
Yes, there are a few.
Custom views are loaded as cnbecom said, using LoadNibNamed. Owner is a proxy class, meaning it just represents "anything". Owner is like a connection that is fitted but not made until you create the class, if that makes sense.
Another method is to create the custom view in code, but instead of having a separate nib, you just re-create the view and change the type in the main Xib/Container Xib. You will have to copy/paste the nib, and that's made easier by embedding it into a view before copying. Even with constraints, this method is easier. Hook up the outlets in the containing Xib.
Third is pure code. This is one primary reason constraints are just extra work, no benefits.
Fourth, is to load the Nib but inside a container. Basically it loads itself into its own type via the AwakeFromNib method. You then have to hook up the outlets to the inner Xib realizing that Owner changed. This method is useful if you can visualize what is inside the white square.
Also I never use constraints. The few times it would be helpful, it's still faster to write a basic single-line of frame x,y code in layoutSubviews or similar. Custom views and manually using layoutSubviews will save you lots of time. Springs and struts are extremely powerful, and iOS lacks the CSS media queries for full layout-changes, just has ways to move constraints around (which modifies existing layout, not helpful if changing menu entirely for two different device types).
Create different xibs for tablet, then write a loading func that tests if on tablet and appends a prefix, will give you an amazing system once you get custom loading down.
Anyway, enjoy! I believe this question is a slight duplicate, as your marker was the frustration with auto-layout, but your real question is "How do I load custom views in Swift", which has been answered many.
There are two ways to that:
Programatically (or writing code might be involved)
create a custom view and include all the interface setup you need including the data and add it to each controller you want it to be shown
Take the advantage of Container view for loading the other content in another controller as in this Github repo
Here is what I did in the second example:
Create a base controller that holds the view that I want to always be shown
Add a view and customize it as required
Add a Container View in the same controller
Add a NavigationController and embed it in the Container View

How do I respond to actions happening in a custom view loaded from xib?

I've followed the instructions in this answer to create a reusable custom UIView laid out with a xib, which I can embed in my Storyboard by referencing the custom class. This works well and I can successfully load the view as advertised. However I want my embedding view controller(s) to be able to connect to IBActions of my embedded view. In the linked example, the custom view receives its own actions but this seems like poor design. I've worked around this by creating a delegate protocol that for custom view, which forwards events to its delegate, but this feels like more work than should be necessary. Additionally, Interface Builder will not allow me to wire up the delegate using references in the UI so I must instead do it programatically.
What I really want is to create a custom IB Action in my custom view, like someActionHappened, then wire that up in the embedding view controller. What is the best way to accomplish this?
A .xib file has a File's Owner, which you can see and select when you're editing it.
When you load the nib, you get to specify the owner.
You can (and must) make the classes of these two things (the File's Owner in the nib and the actual owner at load time) match.
Thus, an action can be hooked from something in the nib to an IBAction in the File's Owner's class, and it will be fulfilled when the nib is loaded. Problem solved.
So:
However I want my embedding view controller(s) to be able to connect to IBActions of my embedded view
So the solution for you is to make your File's Owner (in your nib) and the actual owner (at nib-loading time) be your embedding view controller. Now your embedding view controller is allowed to have an IBAction and you can connect to it in the nib.

reusable ui components using nibs

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?

iOS VIewController for UIScrollViews content

I have a UIScrollView in my app and I am adding some custom views from xib to it so you can horizontally scroll (tabbing) in ScrollView to change which one is shown. For now this works but I have a problem with connecting views to controllers.
I don't know how to choose structure of ViewControllers (how many controllers should I use, use nested controllers,...).
I have a rootView and its controller. In this rootView there is a ScrollView and this ScrollView contains some custom views (subviews) loaded from xib (using loadNibNamed method).
My question is should I use the same ViewController as for rootView also for these subviews in ScrollView? Problem is that the ViewControllers view property is already bind to the rootView (super view in rootView) so when I bind this view property also to subviews an error is occurred. Also if I create new controller for these subviews an error is occurred as well.
When I am loading subviews to the ScrollView with loadNibNamed method in ViewController of rootView, owner of these subviews is ViewController (owner argument of loadNibNamed method is set to self).
Can you tell me please, how should I solve this? What controller should I use for subviews, should I create new one or should I use existing one. Or should I use some nested controller? I am newbie in iOS development so I have a chaos in using ViewControllers right now...
If there isn't much code that is relative to controlling the sub views you could use just the root view controller. i.e A single controller for a single scene would be a good MVC approach.
If you are using it this way , don't change the view property of view controller as this messes it up for the root view - controller setup. If you just need a reference to this views you already have it with the return value of loadNibNamed. Also if you are setting the owner to self then create additional instance variable to hold the sub views(and not the view property) so that you can specify the owner from the xib itself and connect the references appropriately.
However if you have substantial business logic to be written regarding the sub views then its fine to create separate view-controllers(a single class would be fine if all the subviews behave the more or less same way if you are getting what i mean) for it. In the xib for the subviews, you can specify this class as the owner and when using loadNibNamed: you should create an object of the subviewcontroller class and specify this as the owner. This way you can modularize the whole thing.

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/

Resources