Craig Otis
Craig Otis

Reputation: 32104

Connecting drill-down NSSplitView outlets using Storyboards

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.

enter image description here

Upvotes: 13

Views: 2673

Answers (4)

Andriy Gordiychuk
Andriy Gordiychuk

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

ElmerCat
ElmerCat

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.

enter image description here

Upvotes: 0

E. Rivera
E. Rivera

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

Cory
Cory

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

Related Questions