1

This question must be simple, but I am a newbie. I have a splitView that I am trying to manage as a Master-Detail relationship. The user clicks on an OutlineView item in the Master and that triggers a need to update the content of a PopUp button on the Detail side. I have tried several tests of the arrangement in the larger code from which the code below is extracted (See TEST 1, TEST 2, TEST 3, TEST 4 marked in the code below. The one that fails is TEST 3 and it fails because displayListPopUP is nil in the protocol function when it is called by the delegate although it is not nil when the class DetailViewController initially loads the PopUp with the same instructions. How do I repair TEST 3?

import Cocoa

var displayList: [String] = []

class MasterViewController: NSViewController {

    weak var delegate: detailViewControllerDelegate?
    let detailViewController = DetailViewController()

    override func viewDidLoad() {
        super.viewDidLoad()
        displayList = ["Problem(s) in Selected Category","EM3", "EM4"]        
        self.delegate = detailViewController as          detailViewControllerDelegate

    // TEST 1---THIS WORKS (Bingo is printed)
        delegate?.testPrint(val: "Bingo")
}

}

extension MasterViewController: NSOutlineViewDelegate {

    func outlineViewSelectionDidChange(_ notification: Notification){

        displayList = ["Problem(s) in Selected Category","EM1", "EM2"]

    // TEST 2---THIS WORKS (Bingo 2 is printed)
        delegate?.testPrint(val: "Bingo 2")

    // TEST 3---THIS DOESN'T WORK (displayListPopUP is nil)
        delegate?.loadListPopUP(list: displayList)
    }
}

protocol detailViewControllerDelegate: class {
    func testPrint(val: String)
    func loadListPopUP(list: [String])
}


class DetailViewController: NSViewController, detailViewControllerDelegate     {

//I CHECK, XCODE SAYS THIS OUTLET IS ACTIVE
    @IBOutlet weak var displayListPopUP: NSPopUpButton!

    override func viewDidLoad() {
        super.viewDidLoad()

    //TEST 4---THIS WORKS (PopUp button is loaded correctly)
        displayListPopUP.removeAllItems()
        displayListPopUP.addItems(withTitles: displayList)
    }

    func testPrint(val: String){
        print(val)
    }

    func loadListPopUP(list: [String]){
    //THIS DOES NOT WORK.  displayListPopUP is nil when called from delegate
        displayListPopUP.removeAllItems()
        displayListPopUP.addItems(withTitles: list)
    }

}

  • “XCODE SAYS THIS OUTLET IS ACTIVE” well I don’t think it is – matt Apr 12 '18 at 04:12
  • You instantiate your `detailViewController` with `DetailViewController()`. Usually this initializer keeps all outlets unconnected. – OOPer Apr 12 '18 at 04:38
  • @OOper Can you suggest a fix? I tried moving the instantiation up outside the scope of MasterViewController, but that doesn't fix TEST 3.. – Black Feather Apr 12 '18 at 15:43
  • Moving does not fix. Do not use `DetailViewController()`. – OOPer Apr 12 '18 at 20:43
  • But then what do I do with the line of code – Black Feather Apr 12 '18 at 23:49
  • 1
    self.delegate = detailViewController as detailViewControllerDelegate – Black Feather Apr 12 '18 at 23:49
  • @OOper Is there an alternate way to do this? Thanks for your help! – Black Feather Apr 12 '18 at 23:54
  • The reason I wrote _Do not use `DetailViewController()`_ is because there's no clear documentation for the initializer. See the [doc of `NSViewController`](https://developer.apple.com/documentation/appkit/nsviewcontroller). (I guess it is defined the same as `NSViewController(nibName: nil, bundle: nil)` but could not have found any descriptions.) Anyway, the view hierarchy defined in the nib (assuming the initializer binds the view controller with the nib) is NOT instantiated so, the outlets may not be connected yet just after the initialization. – OOPer Apr 13 '18 at 00:11
  • @OOper Added: I tried removing that line of code( self.delegate = ...) and self.delegate goes to nil. Indeed, that is why the instantiation of detailViewController was used in the first place. – Black Feather Apr 13 '18 at 00:13
  • @OOper Is there a way to programmatically reconnect the "view hierarchy" or at least my troublesome PopUp in the new instance? – Black Feather Apr 13 '18 at 00:17
  • The doc of `NSViewController` is not clear enough, but according to the doc of `UIViewController`, you may need to access `view` property to make the view hierarchy loaded. Try: change the property decl as `var detailViewController: DetailViewController`, and insert two lines in `MasterViewController.viewDidLoad()` 1. `detailViewController = DetailViewController(nibName: nil, bundle: nil)` 2. `_ = detailViewController.view`, before the line `self.delegate = ...`. – OOPer Apr 13 '18 at 00:20
  • @OOper Well, the code compiled, but execution crashed with a very long log entry that begins with – Black Feather Apr 13 '18 at 00:53
  • [General] -[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: ProbleMatic.DetailViewController in bundle (null). – Black Feather Apr 13 '18 at 00:53
  • Sorry, but many things depend on how you setup your project including the nib files, and that's why I cannot write an answer. Without knowing the details of your project I cannot say any more. – OOPer Apr 13 '18 at 00:57
  • @OOper Thank you very much for your time and help. You have helped to clarify the underlying issue. – Black Feather Apr 13 '18 at 16:00

1 Answers1

0

This seems to solve the problem:

var detailViewController = DetailViewController()
weak var delegate: detailViewControllerDelegate?

override func viewDidLoad() {
    super.viewDidLoad()

...

detailViewController = 

self.storyboard?.instantiateController(withIdentifier: "detailVC") as! NSViewController as! DetailViewController

 // This is a trick to force the detailViewController to load its hierarchy     view, icluding displayListPopUP

    _ = detailViewController.view

... }

xcode does, indeed, show an active outlet to my PopUp. The initial display of DetailViewController is apparently managed by macOS with its own instantiation of the view. Thus Test 4 does work.

Creating the master-detail relationship apparently requires a new instantiation of my DetailViewController. Since I am using Storyboard, nibs and xibs, if they exist, are out-of-sight and out-of-mind. Thus, @OOper's instantiation had to be replaced with the one shown. But, he is correct, the instantiation does not load the view hierarchy. At that point Test 3 would fail because displayPopUP would still be nil. The view hierarchy is loaded after the first reference to the view and the dummy call that is shown forces the hierarchy to load, with the displayPopUP now active. Test 3 is now successful.