Search code examples
storyboardswift2xcode7nssplitviewcontroller

How to access splitViewItems by item name in Swift + Storyboards


I'm currently using a NSSplitViewController that has a two child split view items (one is a View Controller and one is another Split View Controller as shown:

enter image description here

I would like to access the split view items programatically to change the collapse status (and later some other properties) from the Window Controller.

I can easily access the split view items by position using this:

    let myMainSplitViewController = self.contentViewController as! NSSplitViewController
    let mySplitViewController2 = myMainSplitViewController.splitViewItems[1]
    mySplitViewController2.collapsed = !mySplitViewController2.collapsed

I'm thinking that in the long term using the position (1 in the example) will not be a good idea (for example if I rearrange the split items in the future). Is there a way to access the split view items with a more permanent link thank just the index number?

I'm thinking I could try to assign Storyboard ID to the controllers of the split view items and search for the exact one by name using the array filter method, but this may be a bit cumbersome... is there something easier like this?:

myMainSplitViewController.splitViewItems["My Split View Controller 2"] 

Solution

  • If your split view items have custom controllers you can access them via NSSplitViewController.splitViewItemForViewController(viewController: NSViewController).

    I would declare a protocol for each view controller:

    protocol MainSplitViewControllerProtocol {
      var thisSplitItemController: ThisSplitViewItemControllerProtocol? {get set}
      var thatSplitItemController: ThatSplitViewItemControllerProtocol? {get set}
    }
    
    protocol ThisSplitViewItemControllerProtocol {
      //any contracts you need here
    }
    
    protocol ThatSplitViewItemControllerProtocol {
      //any contracts you need here
    }
    

    Then I'd declare my concrete classes like this:

    class MainSplitViewController: NSSplitViewController, MainSplitViewControllerProtocol {
      var thisSplitItemController: ThisSplitViewItemControllerProtocol?
      var thatSplitItemController: ThatSplitViewItemControllerProtocol?
      var thisSplitViewItem: NSSplitViewItem?
      var thatSplitViewItem: NSSplitViewItem?
    
      override func viewWillAppear() {
        thisSplitViewItem = thisSplitItemController != nil ? self.splitViewItemForViewController(thisSplitItemController as! NSViewController : nil)
        thatSplitViewItem = thatSplitItemController != nil ? self.splitViewItemForViewController(thatSplitItemController as! NSViewController : nil)
      }
    }
    
    class ThisViewController: NSViewController, ThisSplitViewItemControllerProtocol {
      var parent: MainSplitViewControllerProtocol?
    
      override func viewdidLoad() {
        super.viewDidLoad()
        parent = (self.parentViewController as! MainSplitViewControllerProtocol)
        parent?.thisSplitItemController = self
      }
    }
    
    class ThatViewController: NSViewController, ThatSplitViewItemControllerProtocol {
      var parent: MainSplitViewControllerProtocol?
    
      override func viewdidLoad() {
        super.viewDidLoad()
        parent = (self.parentViewController as! MainSplitViewControllerProtocol)
        parent?.thatSplitItemController = self
      }
    }
    

    Now I can access the split view items regardless of their index in the parent's splitViewItems array:

    extension MainSplitViewController {
      func toolbarButtonClick(button: NSButton) {
        thisSplitViewItem?.collapsed = !thisSplitViewItem?.collapsed
        thatSplitViewItem?.collapsed = !thatSplitViewItem?.collapsed
      }
    }
    

    By using protocols I have also decoupled all the view controllers so the views can be swapped out easily. If you need to control the collapsed state of one of the NSSplitViewItems (or any other properties) from another NSSplitViewItem add the relevant NSSplitViewItem as a property to your NSSplitViewController protocol.