2

I am developing an mac osx application which have initial window and viewcontroller launched from main storyboard. I want to replace the content view loaded by storyboard with my view. I am doing this -

func replaceContentView() {
  parentViewController =  MainViewController(nibName: "MainContainerView", bundle: nil)!
  let fullScreenFrame = NSScreen.mainScreen()?.visibleFrame
  self.initialWindow.setFrame(fullScreenFrame!, display: false, animate: false)
  self.initialWindow.contentView = parentViewController! . view
}

Problem with this approach is that the default viewcontroller is never getting deallocated. deinit() of default viewController is not called at all. This is causing memory leak. So how to completely remove default content view and associated viewcontroller?

dave mankoff
  • 17,379
  • 7
  • 50
  • 64
Abhishek
  • 852
  • 9
  • 24
  • NSViewController is different than NSView. You replaced contentView with a new view. The relationship between original NSWindowController and NSViewController remains. – Marek H Aug 03 '15 at 23:03

3 Answers3

1

Storyboards don't deal in views, they deal in viewcontrollers. What a Storyboard does when it loads a view into a window is that it creates an NSViewController and then goes

windowController.contentViewController = theViewController

That implicitly also inserts theViewController.view as the window's content view. So do the same, and all will be fine.

Marek's example is wrong, because CustomView shouldn't be an NSView subclass, it should be a CustomViewController class that owns a view containing the label etc. As a bonus, NSViewController will take care of loading the XIB for you as well.

Alternately, you could set windowController.contentViewController = nil (which will remove the old view controller and its content view) and then set your content view. But really, why fight the framework when that's exactly what NSViewController is intended for?

uliwitness
  • 8,532
  • 36
  • 58
0

You can write the code in deinit method,may it will help you.

 deinit {
// perform the deinitialization
}
nandu kathmandi
  • 33
  • 1
  • 12
0

Your contentViewController within NSWindow instance still holds strongly its old view. You have replaced just property on your NSWindow instance.

To clarify what you have done:

  1. NSWindow holds strongly against new view
  2. NSViewController holds strongly against old view

You should assign your new view into contentViewController.view property as well

This might be helpfull:

NSWindow.h

/* NSViewController Support */

/* The main content view controller for the window. This provides the contentView of the window. Assigning this value will remove the existing contentView and will make the contentViewController.view the main contentView for the window. The default value is nil. The contentViewController only controls the contentView, and not the title of the window. The window title can easily be bound to the contentViewController with the following: [window bind:NSTitleBinding toObject:contentViewController withKeyPath:@"title" options:nil]. Setting the contentViewController will cause the window to resize based on the current size of the contentViewController. Autolayout should be used to restrict the size of the window. The value of the contentViewController is encoded in the NIB. Directly assigning a contentView will clear out the rootViewController.
 */
@availability(OSX, introduced=10.10)
var contentViewController: NSViewController?


/* The view controller for the window's contentView. Tracks the window property of the same name.
 */
@property (strong) NSViewController *contentViewController NS_AVAILABLE_MAC(10_10);

However what you do seems incorrect if you do this on launch.

You either set custom subclass of contentView to your new nsview subclass which can load it's view from another XIB (no need for storyboard).

Abstract example:

class CustomView: NSView {

    @IBOutlet var contentView: NSView!
    @IBOutlet weak var label: NSTextField!

    required init(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        initSubviews()
    }

    override init(frame: CGRect) {
        super.init(frame: frame)
        initSubviews()
    }

    func initSubviews() {
        let nib = NSNib(nibName: "CustomView", bundle: nil)
        nib.instantiateWithOwner(self, topLevelObjects: nil)
        contentView.frame = bounds
        addSubview(contentView)
    }
}

PS: topLevelObjects is set to nil because you hold strongly contentView. So no need to worry about memory management.

Marek H
  • 5,173
  • 3
  • 31
  • 42