How to programmatically subclass a UIViewController stored in a Storyboard? - xcode

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"];

Related

Use NSViewController with Custom View

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.

Swift class to interact with Interface Builder via IBOutlet

Good evening all,
I'm slowly working through my first OS X app. I have been having a hard time getting my Swift class to interact with an NSPopUpButton. Just to make sure I was doing it right, I created a new project and successfully erased all entries and entered text into the NSPopUpButton via AppDelegate. However, as soon as I try to move the same functionality to my own class, I can't even get the IBOutlet connection across to the new class.
Is a particular subclass type required of a new class to work properly with interface builder?
Here is a screenshot of the class I have created, as well as AppDelegate where I am trying to call the function belonging to this class.
Finally, here is the IB element in question, should I be able to select my own class under the 'Custom Class' inspector?
I am an iOS developer, but I would imagine the same principles would apply to your situation.
A ViewController class and an interface created in interface builder are two seperate things. While it may appear that they are connected via an iboutlet, they are actually independent and one can be instantiated without the other.
Currently, you are only creating an instance of your ViewController class in your App Delegate - and that's all. The system has no idea that your xib even exists! The outlets will only be connected once your app connects your xib to your ViewController class.
How do we do this? It's actually quite simple. Instead of instantiating our view controller like this:
let viewcontroller = ViewController()
We would connect our view controller to our xib in the instantiation:
let viewcontroller = ViewController(nibName: "MainWindow", bundle: NSBundle().mainBundle)
The nibName is telling the system the file name of your xib, and the NSBundle().mainBundle is telling the system where to look for the xib.
This will all only work if your xib has been assigned a custom class, like you mentioned. In your xib in interface builder, select the entire view controller. Then, in the custom class inspector type in the name of your ViewController class (in your case: ViewController - it should autocomplete). This will make sure your outlets are connected.
And you should be set up!! Let me know if you have any more problems come up.
Good luck!
EDIT:
This replaces the first part of my answer, however the part about hooking things up in Storyboard remains true. Upon reconsidering, I've believe I've realized that we are only creating the view controller, and not adding it to our view. Despite this, I believe we can take a short cut solution by adding one method to your view controller subclass (the one we set in the Storyboard). Start typing in viewDidLoad, and it should autocomplete. Type in super.viewDidLoad() at the beginning of the method. After that, type self.listUpdate(). This should work if the classes are hooked up correctly in Storyboard. This means you can delete the variables you created in the App Delegate.
Reference: You might also find Apple's documentation on creating a view controller handy (it's in Objective C online, but can be easily converted to Swift - it's the concept that counts): NSViewController Class Reference

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.

Can I use NSViewController to provide a reusable NSTableView with defined columns and data?

I'm fairly new to Xcode and Cocoa development, so please excuse me if I use the wrong terminology.
I have a window which has an NSTableView bound to a NSArrayController. The NSTableView has some predefined columns and the NSArrayController is populated with data when my window is loaded.
This all works fine, but I now need to re-use the functionality in a number of other windows.
After much reading I think and NSViewController is what I need, to provide an object that I can re-use in different windows in multiple .xib.
So what I have done is created a new NSViewController sub class in Xcode which also created a new xib for that controller.
The xib contains a custom view, where I have placed my NSTableView.
#interface KeyViewController : NSViewController
#property (weak) IBOutlet NSTableView *keyTable;
#end
The files owner class is set to the KeyViewController, and the view outlet is set to the files owner.
I then placed a ViewController object into the original window and hooked up the view to a new custom view in the window
I then set the nib name for the ViewController in its properties.
The new view never gets displayed and the view controller initWithNibName never gets called.
Am I missing something vital or missing the plot completely. Should you be able to do this just with interface builder or do I need to alloc and init the view in code?
If I do have to do it in code, whats the purpose of the ViewController object in IB and the Nib Name property for it?
Thanks..

Is it possible to use an object in a nib as a sort of template for dynamically creating multiple new objects?

I would like to design an NSwindow with a WebView in IB for displaying popup links. I need to be able to instantiate any number of these. Is this possible?
Sure. Typically, your window controller would subclass NSWindowController.
MyWindowController* controller = [[MyWindowController alloc] initWithWindowNibName:#"MyWindowView"];
[controller showWindow];
In your nib, set the Owner to MyWindowController, then hook up your outlets as necessary.

Resources