Skip to content

Instantly share code, notes, and snippets.

@mminer
Last active February 5, 2021 19:43
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mminer/caec00d2165362ff65e9f1f728cecae2 to your computer and use it in GitHub Desktop.
Save mminer/caec00d2165362ff65e9f1f728cecae2 to your computer and use it in GitHub Desktop.
NSTabViewController for preferences window that resizes itself to fit activated tab view.
import AppKit
class PreferencesViewController: NSTabViewController {
private lazy var tabViewSizes: [NSTabViewItem: NSSize] = [:]
override func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
super.tabView(tabView, didSelect: tabViewItem)
if let tabViewItem = tabViewItem {
view.window?.title = tabViewItem.label
resizeWindowToFit(tabViewItem: tabViewItem)
}
}
override func tabView(_ tabView: NSTabView, willSelect tabViewItem: NSTabViewItem?) {
super.tabView(tabView, willSelect: tabViewItem)
// Cache the size of the tab view.
if let tabViewItem = tabViewItem, let size = tabViewItem.view?.frame.size {
tabViewSizes[tabViewItem] = size
}
}
/// Resizes the window so that it fits the content of the tab.
private func resizeWindowToFit(tabViewItem: NSTabViewItem) {
guard let size = tabViewSizes[tabViewItem], let window = view.window else {
return
}
let contentRect = NSRect(x: 0, y: 0, width: size.width, height: size.height)
let contentFrame = window.frameRect(forContentRect: contentRect)
let toolbarHeight = window.frame.size.height - contentFrame.size.height
let newOrigin = NSPoint(x: window.frame.origin.x, y: window.frame.origin.y + toolbarHeight)
let newFrame = NSRect(origin: newOrigin, size: contentFrame.size)
window.setFrame(newFrame, display: false, animate: true)
}
}
@1Mr-Styler
Copy link

Nice. Thanks!

@ripperhe
Copy link

Good!

@svoida
Copy link

svoida commented Jan 31, 2020

This is great, thanks!

Is there an easy way to trigger the resize function right away when the window containing the NSTabView appears? I have a weirdly sized window until I click one of the other tabs....

@mminer
Copy link
Author

mminer commented Feb 5, 2020

Hmm, I'm not sure; it's been a while since I've touched AppKit code.

@caraffa
Copy link

caraffa commented Mar 6, 2020

@svoida

this would do the trick (and would also be enough to resize the tabviews. Only problem, it doesn't animate!)

override func tabView(_ tabView: NSTabView, willSelect tabViewItem: NSTabViewItem?) {
    super.tabView(tabView, willSelect: tabViewItem)

    preferredContentSize = (tabViewItem?.view?.frame.size)!
 }

This is how I managed to fix the initial size issue

@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {

    //var settingsWindow: SettingsWindow!
    var settingsWindowController: NSWindowController!

    // MARK: - App Delegate
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Instanciate Preferences Window
        settingsWindowController = NSStoryboard(name: "Settings", bundle: nil).instantiateController(identifier: "SettingsWindowController")
        
        // Set the initial size and title
        if let settingsViewController: SettingsViewController = settingsWindowController.contentViewController as? SettingsViewController {
            settingsWindowController.window?.setContentSize(settingsViewController.tabViewSizes.first?.value ?? NSSize(width: 500.0, height: 500.0))
            settingsWindowController.window?.title = settingsViewController.tabViewItems[0].label
        }
    }
.
.
.
class SettingsViewController: NSTabViewController {
    
    public lazy var tabViewSizes: [NSTabViewItem: NSSize] = [:]

    override func tabView(_ tabView: NSTabView, didSelect tabViewItem: NSTabViewItem?) {
        super.tabView(tabView, didSelect: tabViewItem)

        if let tabViewItem = tabViewItem {
            view.window?.title = tabViewItem.label
            resizeWindowToFit(tabViewItem: tabViewItem)
        }
    }

    override func tabView(_ tabView: NSTabView, willSelect tabViewItem: NSTabViewItem?) {
        super.tabView(tabView, willSelect: tabViewItem)
        
        // Cache the size of the tab view.
        if let tabViewItem = tabViewItem, let size = tabViewItem.view?.frame.size, !tabViewSizes.keys.contains(tabViewItem) {
            tabViewSizes[tabViewItem] = size
        }

        if tabViewItem?.identifier as! String != "NSTabViewDatabaseItem" {
            self.view.window?.styleMask.remove( [ .resizable ] )
        } else {
            self.view.window?.styleMask.insert( [ .resizable ] )
        }
    }
.
.
.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment