1
votes

(IPhone newbie question)

I've subclassed a UIViewController and a UIView, and created a InitWithFrame for the view. My custom controller uses this to initialize its view, by frame, in its loadView function.

I plan to use the controller and view in my code at different places. At times under a navigation controller, toolbar, both, neither etc. This means that the code creating the view in loadView of the controller, should ideally know in what size to create the view.

How do I avoid hard coding these sizes for the different cases? Is there a way to build the surrounding controllers (navigation bar, etc.), then figure out the size of the viewable frame, and somehow only then create my controller and initialize my custom view using this frame size?

If my design is incorrect, any tip could be of great assistance... Thanks!

2

2 Answers

1
votes

For the initial vew size you might be interested in [UIScreen applicationFrame]. Returned rectangle accounts for the status bar and interface orientation. When your controller is used inside a navigation controller or other wrapper that changes the available screen space, I think the view gets resized automatically (through UIViewController magic), so there’s nothing you have to do (apart from setting a permissive autoresizingMask).

0
votes

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 customers only care about "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 class.

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 here. 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 nib file contains another UIView where all of the GUI is laid out, and then in the awakeFromNib: function 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.

  • The widget self-initialzes its user interface by using NSBundle's loadNibNamed:owner:options method in its own awakeFromNib method, so integration of the widget into customer classes is clean: just drop a UIView into the customer's view in IB, change the UIView's class to MyWidgetView, and in the customer's init or viewDidLoad set up any defaults in the widget's display using the widget's API that you write yourself (setScore: setTeamImage: etc.)

It seems to me there is very, very little difference between subclassing a UIView in this way to make a reusable "widget", and using a UIViewController. In both cases the .h file of the widget are full of members, outlets, action declarations, and special API declarations. In both cases the .m file of the widget is full of 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:

  • Make the widget class a subclass of UIView
  • Instantiate the widget wherever you want, in other views in your app in IB directly.
  • Design your widget's UI in IB.
  • in the widget class's awakeFromNib: method, load the widget's UI from xib and do [self addSubview:theXibView]

  • 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.

edit: forgot to mention, 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.