iPhone subview design (UIView vs UIViewController)

2019-02-07 00:28发布

问题:

I'm designing a simple Quiz application. The application needs to display different types of QuizQuestions. Each type of QuizQuestion has a distinct behavior and UI.

The user interface will be something like this: alt text http://dl.getdropbox.com/u/907284/Picture%201.png

I would like to be able to design each type of QuizQuestion in Interface Builder.

For example, a MultipleChoiceQuizQuestion would look like this: alt text http://dl.getdropbox.com/u/907284/Picture%202.png

Originally, I planned to make the QuizQuestion class a UIViewController. However, I read in the Apple documentation that UIViewControllers should only be used to display an entire page.

Therefore, I made my QuizController (which manages the entire screen e.g. prev/next buttons) a UIViewController and my QuizQuestion class a subclass of UIView.

However, to load this UIView (created in IB), I must[1] do the following in my constructor:

//MultipleQuizQuestion.m
+(id)createInstance {
    UIViewController *useless = [[UIViewController alloc] initWithNibName:@"MultipleQuizQuestion" bundle:nil];
    UIView *view = [[useless.view retain] autorelease];
    [useless release];
    return view; // probably has a memory leak or something
}

This type of access does not seem to be standard or object-oriented. Is this type of code normal/acceptable? Or did I make a poor choice somewhere in my design?

Thankyou,

edit (for clarity): I'd like to have a separate class to control the multipleChoiceView...like a ViewController but apparently that's only for entire windows. Maybe I should make a MultipleChoiceViewManager (not controller!) and set the File's Owner to that instead?

回答1:

You're on the right track. In your QuizController xib, you can create separate views by dragging them to the xib's main window rather than to the QuizController's main view. Then you can design each view you need according to your question types. When the user taps next or previous, remove the previous view and load the view you need based on your question type using -addSubview on the view controller's main view and keep track of which subview is currently showing. Trying something like this:

[currentView removeFromSuperView];

switch(questionType)
{
    case kMultipleChoice:
        [[self view] addSubview:multipleChoiceView];
        currentView = multipleChoiceView;
        break;
    case kOpenEnded:
        [[self view] addSubview:openEndedView];
        currentView = openEndedView;
        break;
// etc.
}

Where multipleChoice view and openEndedView are UIView outlets in your QuizController connected to the views you designed in IB. You may need to mess with the position of your view within the parent view before you add it to get it to display in the right place, but you can do this with calls to -setBounds/-setFrame and/or -setCenter on the UIView.



回答2:

Yeah, IB on iPhone really wants File's Owner to be a UIViewController subclass, which makes what you want to a bit tricky. What you can do is load the nib against an existing UIViewController instead of instantiating one using the nib:

@implementation QuizController

- (void) loadCustomViewFromNib:(NSString *)viewNibName {
  (void)[[NSBundle mainBundle] loadNibNamed:viewNibName owner:self options:nil];
}

@end

That will cause the runtime to load the nib, but rather than creating a new view controller to connect the actions and outlets it will use what you pass in as owner. Since we pass self in the view defined in that nib will be attached to whatever IBOutlet you have it assigned to after the call.