views:

88

answers:

3

This is actually a two part question, hope my explanation is clear:

I have an NSViewController which can be configured to show a different custom view on part of its view. For example, its view can show either CustomViewA or a CustomViewB.

I was able to make this work by creating an NSViewController for each of the custom views and initializing MyViewController with either an NSViewController which handles the CustomViewA or an NSViewController which handles the CustomViewB. I use an NSBox and set its contentView to the view provided by the given NSViewController.

The problem with this approach is that I have an NSBox who's contentView will hold the "MyView" and then inside "MyView" I have another NSBox which will hold either the CustomViewA or the CustomViewB.

The other problem is that I'd like MyViewController to handle both the CustomViewA and the CustomViewB, opposed to having a separate NSViewController for each one of them.

Here is sample code of my current solution:

// How I initialize the NSViewControllers
CustomViewControllerA* cvc = [[CustomViewControllerA alloc] initWithNibName:@"CustomViewA" bundle:nil];
MyViewController* controller = [[MyViewController alloc] initWithCustomViewController:cvc nibName:@"MyView" bundle:nil];

//...

// In Controller of main view
- (void)awakeFromNib
{
    // container is an NSBox*
    [self.container setContentView:[self.myViewController view]];
}

//...

//  In MyViewController
-(void)awakeFromNib
{
    // content is an NSBox*
    [self.content setContentView:[self.customViewController view]];
}

How can I have my CustomViewA and CustomViewB live inside MyView.nib and both of them use MyViewController as their Controller?

How could I have the main view hold a MyView instead of an NSBox?

Thanks in advance!

A: 

Try this:

// In MyViewController.m
- (void) awakeFromNib {
  [self.view addSubview:[self.customViewController.view]];
}

and your MyViewController will have to have a parameter such as:

// In MyViewController.h
@interface MyViewController : NSViewController {
  IBOutlet NSViewController* customViewController;
}
Aviad Ben Dov
Thanks, I'll try that! One question though... MyViewController swaps the view when the user clicks a button. I only know how to do this with NSBox setCustomView:In this approach how can I swap the view on the user's click? Calling [self.view addSubview:...] again?
Gabriel Ayuso
More or less. You can call addSubview again, but you also need to call [NSView removeFromSuperview] on the original subview you added.
Aviad Ben Dov
A: 

Here's the way I would probably go about setting this up.

@interface MyViewController : NSViewController
{
    IBOutlet NSView* customView; //initially points to an NSBox or generic custom view from the nib file
    CustomViewControllerA* viewControllerA;
    CustomViewControllerB* viewControllerB;
}

@end

@implementation MyViewController

- (void)switchToCustomViewController:(NSViewController*)newCustomViewController
{
    //Make sure the subview gets put in the right location
    [newCustomViewController.view setFrame:customView.frame];
    [self.view replaceSubview:customView withView:newCustomViewController.view];
    customView = newCustomViewController.view;
}

//I usually use loadView in NSViewControllers rather than awakeFromNib, but either works
- (void)loadView
{
    [super loadView];
    viewControllerA = [[CustomViewControllerA alloc] initWithNibName:@"CustomViewA" bundle:nil];
    viewControllerB = [[CustomViewControllerB alloc] initWithNibName:@"CustomViewB" bundle:nil];
    [self switchToCustomViewController:viewControllerA];
}

@end

So, rather than putting the custom view inside an NSBox, this setup would have a view included in the nib as a temporary placeholder that just gets removed the first time you switch to one of the custom views. Then, when you want to swap, you just call -switchToCustomViewController: with the controller you want to switch to. From then on, when you switch, you're just swapping one custom view for the other as a direct subview of MyViewController's view. If you want to be able to specify which one should be the one used initially, just create an init method or simple property that can be set to tell MyViewController which custom view should be the one used first.

Brian Webster
Thanks for the tip. This works fine except that the autosizing of the custom views are not working with this approach. When I toggle the switch it will resize properly because I'm setting the frame. Does this mean that I have to listen to the view's resizing and set the frame every time? If so... When setting the custom view as an NSBox's contentView the resizing works automatically. Is there an advantage to switching the subviews instead of using an NSBox and switching it's contentView?
Gabriel Ayuso
Just wanted to add that my view does have the "Autoresizes Subviews" flag on. In Interface Builder I added an NSBox to the view to test if it would be resized, and it is. Only the subviews that I add programmatically are not being resized automatically.
Gabriel Ayuso
A: 

NSViewController isn't really intended to handle multiple views that are swapped in and out. It is mostly for handling a single view loaded from a Nib/Xib, managing memory of the top-level objects and providing convenient bindings functionality. The typical usage model is to subclass NSViewController for each view, as you are currently doing.

Preston