Programmatically hide and close the window of current NSViewController - swift

I would like to hide/close my main app window in func viewDidLoad() and only show/unhide the main window if some event requires it.
I tried self.view.window?.close() but this leaves a white window. I also tried NSApp.hide(nil) but then I can't unhide with NSApp.unhide(nil). Here is some sample code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
NSApp.hide(nil)
runTest()
}
func runTest () {
let check = false
if check == false {
NSApp.unhide(nil)
}
}

From the NSWindow documentation
func orderOut(_ sender: Any?)
Removes the window from the screen list, which hides the window.
func makeKeyAndOrderFront(_ sender: Any?)
Moves the window to the front of the screen list, within its level, and makes it the key window; that is, it shows the window.
Hide and Close are two different things:
If the window is the key or main window, the window object immediately behind it is made key or main in its place. Calling orderOut(_:) causes the window to be removed from the screen, but does not cause it to be released. See the close() method for information on when a window is released. Calling orderOut(_:) on a child window causes the window to be removed from its parent window before being removed.
Hiding the application (NSApp.hide(nil) is another different thing: It
Hides all the receiver’s windows, and the next app in line is activated.

Related

How to communicate to the window controller from view controller of newly, programmatically created tab?

In my window controller, I implement:
#IBAction override func newWindowForTab(_ sender: Any?) {
if let wc = NSStoryboard.main?.instantiateInitialController() as? WindowController,
let window = wc.window {
self.window?.addTabbedWindow(window, ordered: .above)
window.makeKey()
}
}
In the view controller, I have this code:
let window = self.view.window?.windowController as? WindowController
Also tried:
let window = NSApp.mainWindow?.windowController as? WindowController
If I don't have any tabs, it's able to get the window controller. But on new tabs, it does grab the window controller.
Similarly, I've unsuccessfully tried sending an action to the WindowController:
NSApp.sendAction(#selector(WindowController.pageLabelChange), to: nil, from: label)
Works for the original window, but not for any newly created tabs.
How do the newly created view controller objects communicate with the window controller?
Edit:
For more context in how I am using this code: It's basically a PDFView that's embedded in a window. The window has a tool bar that displays the page number. Using any of the above code, I can set the current page number of the PDFView, but when there's a tab, it does not work. Using the .PDFViewPageChanged notification, I call my func
NSApp.sendAction(#selector(WindowController.pageLabelChange), to: nil, from: pdfView)
Edit 2:
I've created a GitHub with a test project that shows the problem I have. You should be able to see that when you launch the project, the + button will add a number to the textfield in the tab bar. But if you go to the View menu > Add tab, it creates a new tab, but the + does nothing.
You create new window controller on stack so it is destroyed right after return, that is the reason. You need to store somewhere reference to created tab window controller and manage it (the place of storage is up to your app logic).
Here is simple demo that makes code work
Tested with Xcode 11.4 / macOS 10.15.5
final class WindowController: NSWindowController {
#IBOutlet weak var testField: NSTextField!
var tabControllers = [WindowController]() // << storage for child controllers
#IBAction override func newWindowForTab(_ sender: Any?) {
if let wc = NSStoryboard.main?.instantiateInitialController() as? WindowController,
let window = wc.window {
self.window?.addTabbedWindow(window, ordered: .above)
window.makeKey()
tabControllers.append(wc) // keep reference in storage
// TODO: - you are responsible to manage wc, eg: remove
// from storage on window close.
}
}
}

Detect maximize/minimize window event and do it programmatically

How detect Minimize/Maximize window and do this programmatically. I know that i need use NSWindowdelegate, but:
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowdelegate {
func windowWillMiniaturize(_ notification: Notification) {
print("1")
}
}
This not working. And how to maximize/minimize programmatically have no idea.
It is all in Apples documentation on NSWindowDelegate: https://developer.apple.com/reference/appkit/nswindowdelegate
You can implement in you class that is set as delegate for your window:
func windowWillMiniaturize(Notification)
Tells the delegate that the window is about to be minimized.
func windowDidMiniaturize(Notification)
Tells the delegate that the window has been minimized.
func windowDidDeminiaturize(Notification)
Tells the delegate that the window has been deminimized.
NSWindow has methods - easily to find when visiting the documentation: https://developer.apple.com/reference/appkit/nswindow
You can call from anywhere on your window:
func performMiniaturize(Any?)
Simulates the user clicking the minimize button by momentarily highlighting the button, then minimizing the window.
func miniaturize(Any?)
Removes the window from the screen list and displays the minimized window in the Dock.
func deminiaturize(Any?)
De-minimizes the window.

swift how to delay mac menu bar appearing animation

The Problem:
on a full screen mac app, when you move the mouse pointer to the top, the mac menu bar will instantly drop down. I'd like to make an app which delays the showing of the menu bar by a couple seconds.
It should be possible since that is the exact behaviour that VMWare Fusion does. while in fullscreen, the app lets the mouse pointer sits at the top of the screen for a couple seconds, before dropping down the menu bar.
how would I go about approaching this problem?
---- Update ----
I'm able to hide and show the menu bar when I want using NSMenu.setMenuBarVisible().
However, this doesn't seem to work as I thought it should be if I have multiple window app in fullscreen.
it works fine for one of the fullscreen window, but if I switch to the other fullscreen window I have to move the mouse before the call to setMenuBarVisible(true) seems to take effect.
Code
public class CustomWindowController: NSWindowController, NSWindowDelegate {
override public func windowDidLoad() {
window!.delegate = self
}
public class func create() -> CustomWindowController {
let storyboard = NSStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateControllerWithIdentifier("CustomWindowController") as! CustomWindowController
controller.window?.collectionBehavior.insert(.FullScreenDisallowsTiling)
return controller
}
// MARK: NSWindowDelegate protocol
public func windowDidEnterFullScreen(notification: NSNotification) {
print("window did enter fullscreen")
NSMenu.setMenuBarVisible(false)
}
public func windowDidBecomeKey(notification: NSNotification) {
print("window did become key")
NSMenu.setMenuBarVisible(false)
}
}
public class CustomWindow: NSWindow {
// right now I'm using mouse click to trigger the showing of the menu bar
// but my end goal is to use timer to trigger the showing of the menu bar
// to delay the menu bar showing by a couple seconds
public override func mouseUp(theEvent: NSEvent) {
NSMenu.setMenuBarVisible(true)
}
}
class CustomViewController: NSViewController {
private var otherWindowControllers = [CustomWindowController]()
// button to spawn a second identical window
#IBAction func spawnWindowButton(sender: AnyObject) {
let controller = CustomWindowController.create()
controller.showWindow(self)
// needed at least 1 strong reference to prevent the window to go away,
// so we save the controller to a list
otherWindowControllers.append(controller)
}
deinit {
otherWindowControllers.removeAll()
}
}
Repro:
start the app
click the spawn button to spawn a second window
click the greenlight button to make both window go to fullscreen
go to one of the fullscreened app window
move the mouse to the top of the screen
menu bar shouldn't drop down (intended behaviour)
left click
menu bar should drop down immediately (intended behaviour)
go to the other fullscreened app window
move the mouse to the top of the screen
menu bar shouldn't drop down (intended behaviour)
left click
menu bar doesn't drop down
move the mouse pointer a little bit
now it should drop down
any idea why this happens? and how to fix it so both fullscreen window has the correct behavior

OSX Swift - Show modal second window

I am trying to display a second window after a button click:
var winJ:WinJo // other window NSViewController
#IBAction func BtnNewWin(sender: AnyObject) {
winJ = WinJo()
winJ.showWindow(self)
}
This works fine but I want the new window to be modal. I accomplished this with the Xcode designer but I couldn't figure out how to do this in code.
After I was pointed in the right direction I found the solution to my problem:
NSApp.runModalForWindow(winJ.window!)
Where NSApp is actually the instance of NSApplication.
And very important in the second window:
func windowWillClose(notification: NSNotification) {
NSApp.stopModal()
}
Otherwise your main window will be blocked after closing the second.

Status bar app window deallocation

I'm developing a simple OSX status / menu bar app using this tutorial: http://footle.org/WeatherBar/
This app is going to have a menu with "Preferences" option, which should open the preferences window.
Since the preferences window is going to be opened rather rarely I would like the window to be created only when needed and then deallocated after closing.
Here is the code for the status menu controller which controls showing and creating the preferences window:
class StatusMenuController: NSObject {
#IBOutlet weak var statusMenu: NSMenu!
var preferencesWindowCtrl: PreferencesWindowController!
let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(NSVariableStatusItemLength)
override func awakeFromNib() {
statusItem.title = "MyApp"
statusItem.menu = statusMenu
preferencesWindowCtrl= PreferencesWindowController()
}
#IBAction func preferencesClicked(sender: NSMenuItem) {
preferencesWindowCtrl.showWindow(nil)
/*
THIS CAUSES THE WINDOW TO BE DEALLOCATED IMMEDIATELY:
let myPrefWindow = PreferencesWindowController()
myPrefWindow.showWindow(nil)
*/
}
#IBAction func quitClicked(sender: NSMenuItem) {
NSApplication.sharedApplication().terminate(self)
}
}
In this code window is instantiated in the the status menu controller awakeFromNib, which is something I wanted to avoid (since it makes the window alive for the whole app lifetime). However, if I create it as a local variable inside preferencesClicked it gets deallocated immediately as this functions exists (not really surprising).
How can I make sure this window gets deallocated after it is closed? I guess setting release when closed = true for that window will not help, since the reference is held by StatusMenuController.