Reputation: 32104
I have an NSSplitView
that manages a drill-down hierarchy. The parent/left side displays groups, while the child/right side receives notification that the group selection has changed, and updates to display the child items.
However: When creating an NSSplitView
using storyboards, there are 3 scenes created: One for the split view itself, and one for each of the right/left NSViewController
instances.
The problem here is that I have two controllers that are also acting as NSTableViewDataSource
items, and the parent controller should have an IBOutlet
to the child controller so that it can provide direct notification that the selection has changed.
But! Because these controllers are in different scenes, I cannot connect them. Nor can I move them both up to the parent scene for the split view, because they would then not have access to the NSTableView
outlets. (Nor would the tables reference the controllers as delegates/datasources.)
Do I need to use NSNotification
here? It seems so indirect and wishy-washy, and I haven't found a segue-based approach on the Mac.
Upvotes: 13
Views: 2673
Reputation: 6272
I was having the same problem and have found a workaround. It is not extremely elegant and it does not allow you to connect outlets with storyboard, but it does allow you to create relationships identical to those with IBOutlets
.
The key is to implement a singleton class:
import Foundation
import Cocoa
class SplitViewConnector {
static let sharedInstance = SplitViewConnector()
var masterController:NSViewController?
var detailController:NSViewController?
}
Now in awakeFromNib
method of your master controller you can set a master relationship and, similarly, you can set a relationship for your detail controller.
If, for example, your master controller needs to have a direct relationship to your detail controller you can also implement it. Assuming that the class of your master controller is YourViewController
and the property for accessing your details controller is called myDetail
it would look like:
var masterController:YourViewController? {
didSet {
self.assignDirectConnections()
}
}
var detailController:NSViewController? {
didSet
self.assignDirectConnections()
}
}
func assignDirectConnections() {
if let master = self.masterController {
if let detail = self.detailController {
master.myDetail = detail
}
}
}
Upvotes: 0
Reputation: 3155
You don't necessarily want to use an NSSplitViewController. Instead, just drag an NSSplitView into the main view of your Storyboard.
As Cory explained in his answer, the new NSSplitViewController and NSTabViewController have methods for accessing parent and child controllers. This is useful if you really do want to divide your interface into different Storyboard scenes.
In your example though, you want all of the views to be in the same scene so that you can connect up all their IBOutlets to the same view controller. So, just use an NSSplitView and don't bother with an NSSplitViewController at all.
Upvotes: 0
Reputation: 10938
As you say, Storyboards on OS X (and iOS before that), won't let you connect IBOutlets between scenes so you need to convey the actions in some other way.
I would configure the actions/delegates properties on the awakeFromNib
method of any of the three controllers. At this moment the NSSplitView
and its child controllers will all be created and connected.
I wouldn't use notifications when there are no multiple target objects, or the target objects are known and fixed, as in your case.
Upvotes: 0
Reputation: 2312
The new NSSplitViewController and NSTabViewController class are what Apple is calling container controllers. you can get references to other controllers within the container using the new property's in NSViewController
@property (readonly) NSViewController *parentViewController
@property (copy) NSArray *childViewControllers
so for example if you would like to get a reference to your child Table controller from your parent table controller. In your parent table controller viewDidLoad you can do the following
myChildTableController = [self.parentViewController childViewControllers][1];
or vice versa from your child table controller
myParentTableController = [self.parentViewController childViewControllers][0];
Upvotes: 12