buzo
buzo

Reputation: 81

How to show only Toolbar on NSWindow

I'm new to swift and Xcode: I'm trying to write a cocoa app and can't seem to figure out how to completely hide the contents of my NSWindow. I'm using Swift on Xcode Version 6.3.2; UI designed using storyboards.

Background: I'm designing an app where some controls are shown to the user in the window's toolbar. One of these controls is a disclosure button used for toggling the contents: clicking the button allows the user to expand the window contents underneath the toolbar to see some views with additional details. Clicking the disclosure button again collapses the window contents, hiding the views contained in the window and displaying only the toolbar again. When the app loads, only the toolbar should be shown - all of the window contents should be hidden, as they are irrelevant at this point.

In my WindowController.swift, I have:

class WindowController: NSWindowController, NSToolbarDelegate {

override func windowDidLoad() {
    super.windowDidLoad()

    window?.hideContents()
}

And I've defined an NSWindow extension; in NSWindow.swift I have:

private let TOOLBAR_WIDTH:CGFloat = 350.0
private let TOOLBAR_HEIGHT:CGFloat = 75.0
private let CONTENT_WIDTH:CGFloat = 800.0
private let CONTENT_HEIGHT:CGFloat = 600.0

public extension NSWindow {

    private var screenHeight : CGFloat {
       get {
           return NSScreen.mainScreen()!.frame.height
       }
    }

    public func hideContents() {
        let view = contentView as! NSView
        view.hidden = true
        view.needsDisplay = true

        collapseContentsFrame()
    }

    private func collapseContentsFrame() {

        var newFrame = NSRect(x:frame.origin.x, y:screenHeight, width: TOOLBAR_WIDTH, height: TOOLBAR_HEIGHT )

        setFrame( newFrame, display: false, animate: false )
    }

    public func showContents() {
        let view = contentView as! NSView
        view.hidden = false
        view.needsDisplay = true

        expandContentsFrame()
    }

    private func expandContentsFrame() {

        var newFrame = NSRect(x: frame.origin.x, y: screenHeight, width: CONTENT_WIDTH, height: CONTENT_HEIGHT )

        setFrame( newFrame, display: true, animate: false )
    }
}

This sort of works: it hides the contents of the views contained in the window, and shrinks the frame almost to the same size as the toolbar. However, there is still a wide empty rectangle being shown underneath the toolbar - looks like the window's frame isn't resizing properly (can't post the image as I don't have permission). I've tried tweaking the values on the new frame, but I can't seem to eliminate that extra space shown underneath the toolbar.

In the storyboard the window contents are linked to a TabViewController - not sure if this has something to do with it. Any suggestions/insights would be greatly appreciated.

Upvotes: 2

Views: 1068

Answers (1)

buzo
buzo

Reputation: 81

Thanks to Max and Lucas for their suggestions. I tried removing the content view, keeping a reference to it in my WindowController, and then adding it back again to the window whenever the window is expanded. While this solved the problem at hand, it created new problems for me: I programmatically invoke a segue from the toolbar, but if the window has no content view, this segue invocation crashes.

The solution that ended up working for me involves temporarily removing the constraints on the content view when the window contents are hidden, and then adding the constraints back again when the content view becomes visible. For reference (in case somebody else runs into this in the future), here's what I've done:

In my WindowController.swift:

class WindowController: NSWindowController, NSToolbarDelegate {

    var mainWindow:Window {
        get {
            return window! as! Window
        }
    }

    override func windowDidLoad() {

        super.windowDidLoad()

        mainWindow.hideContents()
    }
}

In a custom NSWindow subclass:

import Cocoa

class Window : NSWindow {

    private let TOOLBAR_WIDTH:CGFloat = 350.0
    private let TOOLBAR_HEIGHT:CGFloat = 75.0
    private let CONTENT_WIDTH:CGFloat = 800.0
    private let CONTENT_HEIGHT:CGFloat = 600.0

    private var constraints:[NSLayoutConstraint] = [NSLayoutConstraint]()

    override init( contentRect: NSRect,
        styleMask windowStyle: Int,
        backing bufferingType: NSBackingStoreType,
        defer deferCreation: Bool) {

        super.init( contentRect: contentRect, styleMask: windowStyle, backing: bufferingType, defer: deferCreation )
    }

    required init?( coder: NSCoder ) {
        super.init( coder: coder )
    }

    private var screenHeight : CGFloat {
        get {
            return NSScreen.mainScreen()!.frame.height
        }
    }

    private var view : NSView {
        get {
            return contentView as! NSView
        }
    }

    func hideContents() {

        view.hidden = true

        collapseContentsFrame()

        removeConstraints()
    }

    private func collapseContentsFrame() {
        var newFrame = NSRect(x:frame.origin.x, y:screenHeight, width: TOOLBAR_WIDTH, height: TOOLBAR_HEIGHT )
        setFrame( newFrame, display: false, animate: false )
    }

    private func removeConstraints() {
        constraints = view.constraints as! [NSLayoutConstraint]
        for constraint in constraints {
            view.removeConstraint( constraint )
        }
    }

    func showContents() {

        view.hidden = false

        expandContentsFrame()

        addConstraints()
    }

    private func expandContentsFrame() {
        var newFrame = NSRect(x: frame.origin.x, y: screenHeight, width: CONTENT_WIDTH, height: CONTENT_HEIGHT )
        setFrame( newFrame, display: true, animate: false )
    }

    private func addConstraints() {
        for constraint in constraints {
            view.addConstraint( constraint )
        }
    }
}

Then in my Main.storyboard, I select the Window node in the document outline and bring up the identity inspector (CMD + ALT + 3). Then I specified my "Window" class in the "Custom Class" field.

Upvotes: 1

Related Questions