swiftcocoansviewcontrollersubviews

Correct release NSViewController embedded in subviews


I have a task to write a small app where various controllers must be embedded in default controller. All controllers stored in one storyboard.

Sample code of embedding controllers in subviews


    if let id = getControllerId(pageIndex) { // get controller's storyboard id by segmented index
      let storyBoard = NSStoryboard(name: "Main", bundle: nil)
      let controller = storyBoard.instantiateControllerWithIdentifier(id) as! NSViewController
      controller.view.translatesAutoresizingMaskIntoConstraints = false
      if self.view.subviews.count > 0 {
        let prevView = self.view.subviews[0]
        prevView.removeFromSuperview() // here should be releasing previous controller
      }
      self.view.addSubview(controller.view)
      // make all side constraints
      let views = ["view": controller.view]
      self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-(0)-[view]-(0)-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
      self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(0)-[view]-(0)-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
    } else {
      NSLog("ERROR: Unable get controller storyboard id for index \(pageIndex)")
    }

And I noticed that embedded controller don't execute viewWillDisappear. I need this event for clearing observers and some other stuff.

I'm not sure it's correct way to show controllers as embedded in subviews, but i don't found any other solution.

I make sample project to test this situation

https://github.com/avvensis/embeddedviewcontrollers

Could anybody help me with this trouble?


Solution

  • viewWillDisappear wont execute as you dont hide anything.

    You controller dies quickly after you create him. So step #1 is to hold a reference on it:

      class ViewController: NSViewController {
    
    // MARK: - Custom properties
    
    let pageIds: [String] = ["redController", "yellowController", "greenController"]
    
    var currentControler : NSViewController!
    

    ...

    private func showEmbeddedController(pageIndex: Int) {
    if let id = getControllerId(pageIndex) { // get controller's storyboard id by segmented index
      let storyBoard = NSStoryboard(name: "Main", bundle: nil)
      currentControler = storyBoard.instantiateControllerWithIdentifier(id) as! NSViewController
      currentControler.view.translatesAutoresizingMaskIntoConstraints = false
      if self.view.subviews.count > 0 {
        let prevView = self.view.subviews[0]
        prevView.removeFromSuperview() // here should be releasing previous controller
      }
      self.view.addSubview(currentControler.view)
      // make all side constraints
      let views = ["view": currentControler.view]
      self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("|-(0)-[view]-(0)-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
      self.view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat("V:|-(0)-[view]-(0)-|", options: NSLayoutFormatOptions(rawValue: 0), metrics: nil, views: views))
    } else {
      NSLog("ERROR: Unable get controller storyboard id for index \(pageIndex)")
    }
    }
    
    
    }
    

    Then just have deinit method in your base controller:

     class EmbeddedViewController: NSViewController {
    

    ....

    deinit {
        print("DEBUG: \(self.className) deinit")
    }
    
    }