Use NSViewController with Custom View - macos

I have a main .xib view with parts of it being made up of custom views. At the same time I have also created separate .xib subviews (together with their respective .h and .m files). These custom classes were then connected to the custom views in the main .xib
This setup works fine however I would like to have a number of NSViewController(s) control each of the different custom views. What is the recommended way to do this?
Eg. Main.xib > contains 'custom views' > each using an NSView custom class and designed in it's own .xib
An NSViewController class would respond to events occurring in one of these custom views instead of the NSViewController tied to Main.xib

I've ended up creating NSViewControllers with XIB files instead of the custom views I had previously.
I then created NSBox components for every custom view that I had. I connected each of these to the main NSViewController via IBOutlet(s).
Finally, I attached each custom view to the dedicated NSBoxes via the IBOutlets as follows:
- (void) awakeFromNib{
[super awakeFromNib];
//instantiate custom view controller
CustomViewController* customViewController = [[CustomViewController alloc] initWithNibName:#"CustomViewController"
bundle:nil];
[self.customNSBoxView setContentView:[customViewController view]];
}

What I've tended to do is just create a new NSViewController subclass and choose the 'Create XIB' option.
From there, I instantiate the view controller subclass and add it to the view hierarchy in code.
This doesn't completely do what you are suggesting, but it does keep things more modular / easier to test. The downside is that its challenging then to setup constraints between the parent and children. I've tended to do this manually as well, or add the subcontroller's view into a NSStackView which gives you some constraints.
There's probably a better way, but this is what we used in our multi-xib project.

Related

Xcode Interface Builder - reusing a UIView

Is it not possible to reuse a UIView with constraints etc, from one view to another. I have tried:
Note: I am not in storyboard, but using xib file in a framework that I am creating - therefore I am using only UIView from xib files.
Tried to reference view in the same xib file.
#IBDesignable incl all custom classe setup.
I am only looking for reusing a UIView incl. constraints.
Regards
You can't do this through the storyboard because constraints are not made to be generalized through viewcontrollers. If you copy and paste constraints, they'll look for the exact views they used to constrain which won't be there anymore.
However if you have multiple views that look similar, then my suggestion would be to make a base class where you layout the view once on the storyboard and then subclass it. Or, your other option would be programmatic.

How to make a modular NSViewController based disclosure view where all disclosed views are contained in their own NIB?

In real apps the view hierarchy can be complex at it really helps to be able to put different views in different nibs. I am following InfoBarStackView example project they give a really nice example of how to use the new NSStackView class which hosts different views. They make a DisclosureViewController which is responsible for hosting a content view
changing the it's size so that is can go from a open to closed state.
Here is a simplified example. What we have are two separate nibs:
DisclosureViewController
ContentViewController
What is the simplest way to load the content view inside the placeholder view of the disclosure view? Is it possible to do this only in IB only?
Currently my AppDelegate has a lot of redundancy because it need to hold references to both view controllers. I wondering if there is a way of simplifying the situation? For this simple example, the AppDelegate would load from the two different nibs using code like this,
// In AppDelegate.m
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
[_disclosureView1.view replaceSubview:_disclosureView1.placeholder with:_contentView1.view];
[(NSView*)_window.contentView addSubview:_disclosureView1.view];
}
You can have NSViewControllers in your main XIB corresponding the the views you want in your NSStackView — in XIB’s inspector you can set the name of the other XIBs they should load to get their ‘view’s.
Assuming you had IBOutlets onto these viewControllers in your main XIB, it’d be as easy as calling:
/* load the views into the stack view */
_stackView = [NSStackView stackViewWithViews:#[_viewControllerOutlet1.view, _viewControllerOutlet2.view, _viewControllerOutlet3.view]];

How to programmatically subclass a UIViewController stored in a Storyboard?

I'm working on my first iPad application and using Storyboards for the first time.
I've got UITableViewController in my Storyboard that uses "Dynamic Prototypes" for custom cells.
What I want to do is programatically instantiate my UITableViewController subclasses but loading the super view controller from the Storyboard.
This is because I have a single UI but multiple subclasses for specific functionality that I need for each different instance.
This was very easy to do with .xib files, I would write the following code:
MyViewControllerSubClassA *viewControllerA = [[MyViewControllerSubClassA alloc] initWithNibName:#"generalViewControllerNib" bundle:nil];
MyViewControllerSubClassB *viewControllerB = [[MyViewControllerSubClassB alloc] initWithNibName:#"generalViewControllerNib" bundle:nil];
I know I can assign a subclass in the Storyboard editor when clicking on the View Controller but I want to set the subclass programmatically when instantiating.
This seems impossible to do with Storyboards as they are instantiated automatically.
This makes the entire concept of Storyboards seem flawed and not very OO.
If I move the View Controller out of the Storyboard and into a .xib file I lose the ability to use Dynamic & Static Prototypes cells as these are supported only when using Storyboards. Also Apple's documentation basically says use Storybaords from now on.
I would try something like this:
MyViewControllerSubclassA *controllerA = (MyViewControllerSubclassA *)[self.storyboard instantiateViewControllerWithIdentifier:#"myGenericVC"];

NSTableView makeViewWithIdentifier across nibs

I have a similar question to Cocoa - View-Based NSTableView, using one cell in multiple tables, amplified by
Apple's own docs for makeViewWithIdentifier:owner:
"Typically identifier is associated with an external NIB in Interface Builder and the table view will automatically instantiate the NIB with the provided owner."
This seems to imply that you should be able to store the NSTableCellView in a separate nib from the nib containing the NSTableView. However, in my experimenting, I have only ever been able to obtain cells which are contained within the tableview I'm calling this on. I.e., if I cut and paste my cell into a new .xib file, the tableview can no longer find it. What am I doing wrong, or is this actually impossible and I am somehow misreading Apple's docs?
Use - (void)registerNib:(NSNib *)nib forIdentifier:(NSString *)identifier to register a nib to be used with a cell identifier.
If it doesn't work you're probably registering the nib after the tableView data has been loaded. Use [tableView reloadData] afterwords to be sure it's not a timing issue.
I just ran into this problem and I think you cannot use makeViewWithIdentifier:owner: when you're using a dedicated Nib to populate View-Based Tables.
The problem has to do with file owners (ie. view controllers). makeViewWithIdentifier:owner: seems intended to be used with "self" as the owner for simple custom views.
Generally if you have a separate nib for the custom view with outlets, you're going to want a separate view controller too. Otherwise, if your custom view has an outlet and the table displays many custom views, which outlet are you referring to from the "self" table view owner?
So in my test, I've got the AppDelegate as the delegate/datasource of the Table View. I have a CellView.xib, and CellViewController.h/.m with outlets to the interface. Then in my tableView:viewForTableColumn:row: delegate method I have this code:
SSCellViewController *vc = [[SSCellViewController alloc] initWithNibName:#"CellView" bundle:nil];
return vc.view;
What you lose is the cell re-use that happens automatically with makeViewWithIdentifier:owner:. To implement that yourself, you'll also likely have to deal with managing the many view controllers you've created.
I might still be missing something, as I'm coming to OS X development after years of only doing iOS work.

Why do UIViewControllers have xib files and UIViews do not?

When I create a new UIViewController in xcode, it offers to make me an associated nib for the interface. However, when I create a UIView, it does not. If my understanding of MVC is correct, views should really be the parts that contain the interface elements (i.e. the nib) while view controllers are the parts that hook the functionality to the views they control.
I'm sure I'll be able to get it working together either way, so this is more of an exploratory question.
This seems like a case where I'm missing some fundamental understanding of how these two parts should be used. What am I missing?
Because UIView is usually not used in such way.
However How do I associate a nib (.xib) file with a UIView?
The answer I eventually got that satisfied my interest was roughly this:
The job of a view controller is to manage a view hierarchy. Interface Builder is an excellent tool for creating view hierarchies. Xcode offers to create a .xib when you create a new view controller because chances are high that you'll want to load the controllers' views from a .xib.
.xib files aren't necessarily meant to hold every UIView (or subclass) that goes into the view, just a general outline of views that don't change during the life of the view. The other UIViews are much easier to create programmatically since they change often.
I had a similar confusion. Basically (according to the MVC) the View is contained inside the Controller. In the iPhone this means that UIViewController has a property called 'view' which is a UIView instance.
A XIB file (and this is not mentioned often) is a serialised UIView instance. It is roughly an XML sub format which represents a UIView with all its subsequent views. So when you create a UIViewController, a UIView is created in the form of a XIB and bounded to that controller.
A new UIView therefore does not have a XIB because they are essentially the same thing...

Resources