Last active
May 19, 2024 05:06
-
-
Save mminer/caec00d2165362ff65e9f1f728cecae2 to your computer and use it in GitHub Desktop.
NSTabViewController for preferences window that resizes itself to fit activated tab view.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
Good!
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....
Hmm, I'm not sure; it's been a while since I've touched AppKit code.
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
Nice. Thanks!