I want to create a reusable component (a custom control) for the iPhone. It consists of several standard controls prearranged on a View, and then some associated code. My goals are:
Let me be more concrete, and tell you specifically what my control is supposed to do. In my app, I sometimes need to hit a web service to validate data that the user has entered. While waiting for a reply from the web service, I want to display a spinner (an activity indicator). If the web services replies with a success code, I want to display a "success" checkmark. If the web service replies with an error code, I want to display an error icon and an error message.
The single-use way to do this is pretty easy: I just create a UIView that contains a UIActivityIndicatorView, two UIImages (one for the success icon and one for the error icon), and a UILabel for the error message. Here's a screenshot, with the relevant parts marked in red:
I then wire up the pieces to outlets, and I put some code in my controller.
But how do I package up those pieces -- the code and the little collection of views -- so that I can reuse them? Here are a few things I found that get me partway there, but aren't that great:
WebServiceValidatorController
) with associated XIB file. That actually feels really promising, but then at that point I can't figure out how, in Interface Builder, to drag this component onto other views. The WebServiceValidatorController
is a controller, not a view, so I can drag it into a Document Window, but not into a view.I have a feeling I'm missing something obvious...
Here is how I'm solving a similar problem: I wanted to:
create reusable, self-contained "widget module classes" implementing a complex view built from multiple UIKit components (and other nested widget module classes!). The higher-level customer classes of these widget classes don't care about what's a UILabel, what's a UIImageView internally within the widget, the customer classes only care about concepts like "displaying the score" or "showing the team logo."
within a widget module class, lay out the UI for the widget and hookup outlets using interface builder
for higher level customers of a widget, I wanted to be able to place the frame of the widget within the view of the customer in interface builder without having to design custom plugins to IB, etc.
These widgets are not top level controllers: so it makes no sense for them to be subclasses of UIViewController. Then also there's the Apple advice not to have more than one VC on a visible screen at a time. Also, there's so much advice and opinion floating around like "MVC! MVC! You must separate your View and your control! MVC!" that people are so strongly discouraged from subclassing UIView or ever placing app logic within a UIView subclass.
But I decided to do it anyway (subclass UIView). I've been around the block a few times (but fairly new still to the iPhone SDK/UIKit), and I'm very sensitive to design that is ugly, and that can cause problems, and I frankly don't see the problem with subclassing UIView and adding outlets and actions. In fact there are many advantages to making your reusable widget be a subclass of UIView rather than be UIViewController-based:
You can place a direct instance of the widget class in a customer view's interface builder layout, and the widget view's UIView frame will be properly initialized when the customer class is loaded from the xib. This is much cleaner to me than putting a "placeholder view" in the customer, then instantiating the widget module programmatically, and setting the widget's frame to that of the placeholder, and then swapping the placeholder view for the widget's view.
You can create a clean and simple xib file to lay out the widget's components in interface builder. The File's Owner is set to your widget class. The nib file contains a root (vanilla) UIView where all of the GUI is laid out, and then in the awakeFromNib: method of the widget, this nib view is added to the widget itself as a subview. Any frame size adjustments can be handled here, entirely within the widget code, if any is necessary. Like so:
- (void) awakeFromNib { [[NSBundle mainBundle] loadNibNamed:@"MyWidgetView" owner:self options:nil]; [self addSubview:self.view]; }
It seems to me there is essentially no difference in the resulting code between subclassing a UIView in this way to make a reusable "widget", and using a UIViewController. In both cases the .h files contain widget class members, outlets, action declarations, and special API declarations. In both cases the .m file contains action implementations, and special API implementation. And the view IS separated from the control -- the view is in the xib file!
So, in summary, this is what I do for packaging up complex reusable view widgets:
in the widget class's awakeFromNib: method, load the widget's UI from xib and do [self addSubview:self.theXibRootView]
Code the widget just like you would a UIViewController: outlets, actions, special APIs
I'd be interested in hearing about other structural systems for creating reusable widgets, and what the advantages are over this approach.
If the widget needs to report events to the customer class, just use a delegate protocol where the customer sets the widget's delegate to self, just like in a UIViewController.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With