5 approach to load UIView from Xib
After the past few years I found that the only manageable way for creating/maintaining view (or any UI element in more general) is to instantiate the UIView from Xib file. Creating/designing is far more intuitive in Interface Builder than write layout code, defining layout constants (dimensions, colors), or even worse introduce magic numbers to nudge the elements all around.
I’m planning to introduce 5 methods below, I’ve been used each of them in different circumstances over the times.
1. The plain way (the poor man’s method to load UIView from XIB)
This approach works only in really the case when you don’t need anything else just a view assembled in Interface Builder without any communication bindings. It has actually no any particular advantages unless it’s relatively easily understanding in the beginning of the Cocoa learning process.
It uses [NSBundle loadNibNamed:owner:options] method with no respect only to the first parameter. Just include the lines below anywhere in you controller’s implementation.
In interface builder you don’t have to setup anything special other than a single (!) customized view that you want to instantiate in you controller. No bindings, even there is no need to specify File’s owner class. You have to write you own hardcoded layout code in return (as you may noticed above).
2. The referenced way (a bit more explicit)
This method is a next step compared to the plain way, since it defines an explicit reference to the view we need. A bit cumbersome that you have to define an outlet property in your controller class to hook up the view with. This point makes this method too specific, or can say unportable.
The shiny part here is that you can define a context view (actually a wrapper) in Interface Builder. That can be really useful to define the contextual layout information for the view in the XIB file (much more convenient than coded layout). But in the same time you have to be aware of the Interface Builder setup. File’s Owner must be set to the instantiating controller’s class and the referencedView outlet must be bound to the actual view you need.
Be warned not to hook up the controller’s view outlet to the wrapper view (nor if it feels instinctually right), since that would reassign the controller’s view at instantiation with this empty one.
This approach is also known as a UITableViewCell instantiating method (without the wrapper view), by adding a UITableViewCell to the XIB file, though, it is not the scope of the present article.
3. Connected actions (some addition for the above actually)
Having a setup like above, you can easily hook up actions to the client controller sent by objects in the custom view. This could be useful, although, it still forces the view to cooperate with a given type of controller. So just define an IBAction in the main controller like below.
4. Encapsulated instantiation (a step toward enlighting controller code)
Controller codes tend to be complicated. Period.
As you incorporate new features, your controller code immediately starts to grow, which you obviously strive to avoid. A step toward keep client code clean is to create a subclass for the custom view, and start to factor the instantiating features down.
The first trick here is to remove that File’s Owner dependency, introducing a tiny little class EPPZSubclassedViewOwner with the sole purpose of referencing the right view among XIB content. It nor even need a separate file as it is specific for this type of custom view. It lifts up the owning role from the controller’s shoulder.
So as a benefit, we can introduce a class method that instantiates the given view presentInViewController:, then adds it to the view hierarchy. If you need different XIBs, like separate interfaces for iPhone and iPad, you can include it here as well, instead of littering the controller’s code around.
In addition, the dismissal for the view dismiss can also moved down here, as it has nothing to do with the controller itself. In the implementation we can tackle the whole instantiating in place, you can see the owner object in action below.
As a result, you can watch the client code cleaning. Much better, with ain’t no custom view related properties in the controller.
It started to look like a reusable code, but we might still need some communication from the view towards the controller.
5. Encapsulate everything (a really flexible, reusable way to load your custom UIView from XIB)
As we successfully separated the view from the controller above, we follow this approach regarding actions as well. To achieve this we introduce a thin protocol declaration <EPPZDecoupledViewDelegate> that introduces the features to the controller, and assure the view that controller will respond to it’s messages, just as every protocol does. It contains two calls decoupledViewTouchedUp: and decoupledViewDidDismiss: at this particular case.
The impelmentation now should keep a reference delegateViewController for the controller, so it can forward the actions. You need to indicate that the controller has to implement delegate methods, so you’ll declare the type as UIViewController <EPPZDecoupledViewDelegate>. The rest is the same as before.
So having this, you can setup a completely independent XIB file that knows nothing (!) about it’s context. It instantiates itself, hooks up their actions on it’s own. It is reusable, can be instantiated from any kind of UIViewController that implements its protocol, which is stated clearly in the header.
The actions themselves doing not too much here, other that they invoke the implemented delegate methods in the controller, so it can customize its own features within, a pretty straight / strict / formal delegate pattern.
To make it more readable and explicit, we can move some declarations down to the .m file, so the header for our shiny custom view is just embodies only the client needs to know about it.
So usage in the client controller just reflects these pretty neat declarations. You have to indicate that the controller gonna implement the view’s delegate features, so you include <EPPZDecoupledViewDelegate> to the interface.
Tada! A beautiful UI module for your project that you can customize independently leaving the controller’s code alone. Having this clients of this class never have to know anything about the XIB content, nor hook up anything within.
Interface Builder setup is nearly the same as you may figured out, but as an overview, you can find the whole project with all the five methods at GitHub.
As it is common with flexible code, it needs a bit more code under the hood, but anyway sooner or later these code lines will land in a collection of reusable classes of one’s own everyday framework.
This is a rough skeleton for the method, in production these classes are likely to implement presenting/dismissal animation, a model object to configure with, sometimes some UI logic shaping a more sophisticated experience, or weak delegate messaging without explicit protocol, but this is yet another story.
支付宝扫一扫捐赠
微信公众号: 共鸣圈
欢迎讨论,邮件: 924948$qq.com 请把$改成@
QQ群:263132197
QQ: 924948