Present modal from NSWindowController without using Storyboards - swift

I building a macOS framework that's running at the beginning of the login process and I want to show some information to the user in a window. I have an NSWindowController with a .XIB file and it's the main window for my framework.
I would like to add more presentAsSheet to another view. For that, I created an NSViewController with a .XIB to define the user interface that an I want to present in a modal but here I'm completely blocked because getting the contentViewController of the window its always nil
I'm initializing my main window like this:
func run() {
NSApp.activate(ignoringOtherApps: true)
mainWC = MainWC(windowNibName: "MainWC")
guard mainWC.window != nil else {
return
}
NSApp.runModal(for: mainWC.window!)
}
Where Im trying to present the other NSViewController"
self.window?.contentViewController?.presentAsSheet(customViewController)
In my MainWindowController I'm doing this:
#IBAction func btnAction(_ sender: Any) {
let customModal = CustomModal(windowNibName: "CustomModal")
self.window?.beginSheet(customModal.window!, completionHandler: { code in
print(message: "Clicked")
})
}
The modal never appears

You can't present the view until the window and the content view have been loaded
Either inside the window controller in windowDidLoad()
override func windowDidLoad() {
super.windowDidLoad()
// present the view
}
or even in viewDidLoad of the view controller
override func viewDidLoad() {
super.viewDidLoad()
// present the view
}

Related

Present NSWindow as sheet from another NSWindow using beginSheet function

I'm building a macOS bundle app that's running at the beginning of the login process and I want to show some information to the user in a window. I have a NSWindowController with a .xib file which it's the main window.
For that, I created an NSViewController with a .xib to define the user interface that I want to present as a sheet, but do not show any UI and never appears
Main NSWindowController looks like:
import Cocoa
class Main: NSWindowController {
var sheetWindow: SheetWindow?
override func windowDidLoad() {
super.windowDidLoad()
self.window!.canBecomeVisibleWithoutLogin = true
self.window!.isMovable = false
sheetWindow = SheetWindow.init(windowNibName: "SheetWindow")
}
#IBAction func btnClick(_ sender: Any) {
self.window!.beginSheet(sheetWindow.window!, completionHandler: { (resp) in
....
})
}
}
==================================================================================
import Cocoa
class SheetWindow: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
self.window!.isMovable = false
}
}
The main window looks like:
And after click the button looks like:
I notice after "beginSheet" function is triggered the close button from the main window disappeared
NOTE: "Visible At Launch" property is unchecked in the SheetWindow .xib
Any idea what it could be happening !!!

windowWillClose and button action not called Swift

I'm designing a mac app with Xcode 10 (beta) and I got an issue with the Preference Window Controller
I have in my Main.storyboard a NSWindowController of custom class PreferenceWindowController with a toolbar. Here are its connections :
Here is the full class :
class PreferenceWindowController: NSWindowController, NSWindowDelegate {
#IBAction func didClickAuthor(_ sender: Any) {
print("author")
}
#IBAction func didClickTypo(_ sender: Any) {
print("typo")
}
override func windowDidLoad() {
super.windowDidLoad()
}
func windowWillClose(_ notification: Notification) {
print("willClose")
}
}
The window is initiated via the AppDelegate class with this code :
let storyboard = NSStoryboard(name: "Main",bundle: nil)
if let wc = storyboard.instantiateController(withIdentifier: "PreferenceWindowController") as? PreferenceWindowController
{
wc.showWindow(self)
}
The window opens as expected, with the toolbar clickable, but no functions from PreferenceWindowController are called at all, neither the closing of the window, nor the clicks on the toolbar.
I checked every connections, every class name, and I really don't know what's wrong...
SOLUTION
The solution is to store the PreferenceViewController class inside the AppDelegate class as a variable.
My solution :
var preferenceWindowController:PreferenceWindowController? = nil
#IBAction func clickPreferences(_ sender: Any) {
if let wc = storyboard.instantiateController(withIdentifier: "PreferencesWindowController") as? PreferenceWindowController {
let window = wc.window
preferenceWindowController = wc
wc.showWindow(self)
}
}
Thank you for helping !
The comment above seems like it could be on the right track. Based on the code context you've included in your question, it looks like the window controller you create will only have a lifetime for that function call.
Try making the window controller an instance variable. This is normally how I wire things up in an App delegate that creates window controllers. It's a simple pattern that works well.

What is the correct way to make a NSWindowController Singleton in Swift?

I have a sample project as:
https://github.com/ericgorr/nspanel_show.git
My project is a storyboard, document based application. I would like to use a custom segue to toggle the visible state of the inspector window. What I have should work, but I cannot quite determine how to make the inspector window a singleton.
I believe I should start with:
class InspectorWindowController: NSWindowController
{
static let sharedInstance = InspectorWindowController()
// override func init()
// {
//
// }
override func windowDidLoad()
{
super.windowDidLoad()
NSLog( ":::: %#", InspectorWindowController.sharedInstance );
}
}
But exactly what the initialization should look like in my situation is escaping me, especially since the window is inside of a storyboard.
You can select the window controller from the window controller scene and in the attributes inspector select Single from the pop up under Presentation. This will ensure the show segue only uses a single instance of the window controller. See this answer for more information.
Here's how I would modify your code:
In Main.storyboard give your InspectorWindowController an identifier, such as "Inspector Window Controller"
In InspectorWindowController, implement your singleton as follows:
static let shared: InspectorWindowController = {
let storyboard = NSStoryboard(name:"Main", bundle: nil)
let controller = storyboard.instantiateController(withIdentifier: "Inspector Window Controller")
return controller as! InspectorWindowController
}()
In Main.storyboard delete the segue from WindowController to InspectorWindowController
In WindowController replace the showMyPanel() and hideMyPanel() IBActions with:
#IBAction func toggleInspectorPanel( _ sender: AnyObject ) {
let inspectorWindow = InspectorWindowController.shared.window!
if inspectorWindow.isVisible {
inspectorWindow.orderOut(self)
} else {
inspectorWindow.makeKeyAndOrderFront(self)
}
}
Also in WindowController, remove the NSLog() call from windowDidLoad(). It causes a recursive call to the InspectorWindowController.shared initialization code.
In Main.storyboard link the Inspector toolbar button to toggleInspectorPanel()
The InspectorWindowController.shared singleton will be initialized, and the inspector panel loaded (but not shown), the first time it is referenced.

How can I dismiss the parent view controller after the second view controller is launched?

I am working on an OSX app in swift which programmatically clicks a button to launch a view controller. I would like to dismiss the initial view controller programatically.
class ViewController: NSViewController {
#IBOutlet weak var button: NSButton!
override func viewDidLoad() {
super.viewDidLoad()
button.performClick(nil)
self.dismissViewController(self)
// Do any additional setup after loading the view.
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
}
Just to upgrade the previous answer
self.view.window?.close() doesn't work in swift 4 you might want to try the code below.
self.view.window?.rootViewController?.dismiss(animated: false, completion: nil)
It looks like your button isn't actually performing dismissViewController(). You're actually calling this method in your viewDidLoad(). It's also worth mentioning that you might want to try self.view.window?.close() instead.

How do you make a modal view appear outside of a controller?

I am trying to program a little game just to apply the concepts I learned in this course myself. When the game opens up, I would like for a custom modal view to tell the user how to play. Likewise, when they lose, I want to present a results page, which would be inside an if statement. I've searched all over the internet, and I can't find a way to display these views without an error. All that this video shows is how to show a display a view when a button is pressed; how do I display a custom modal view on command in code? (I am very new to Swift, so try to put it in layman's terms.) Thanks!
import UIKit
var numberValue = 0
let randomInt = getRandomNumber()
class ViewController: UIViewController {
#IBOutlet weak var buttonLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("HowToPlayView") as! HowToPlay
self.presentViewController(vc, animated: true, completion: nil)
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func buttonPressed() {
numberValue += 1
updateButtonValue(buttonLabel)
}
}
#IBAction func buttonPressed() {
numberValue += 1
updateButtonValue(buttonLabel)
}
}
That's my view controller. Getting error: Thread 1: EXC_BAD_ACCESS
My view in interface and connections
enter image description here
Ok, let's try a different approach to solve this issue. Above your ViewController you should see three different buttons like this:
Click on the first button and Ctrl + Drag to the view that you want to display and choose display modally. After that is done you should see this arrow appear:
This is called the segue from the first view to the second view and you can call this in your code to transition from one view to the next, but first we have to give our segue an identifier. You can do this by clicking on the arrow and a window should come up like this:
As you can see you will have to fill out the identifier field, and in my case I just named it "myAwesomeSegue".
Lastly, in the first ViewController run this code when you need to present the next view controller:
performSegueWithIdentifier("myAwesomeSegue", sender: nil)
Please note that if you use this method just delete the previous code you had before as this is a brand new approach (so basically delete this code):
let vc = self.storyboard?.instantiateViewControllerWithIdentifier("HowToPlayView") as! HowToPlay
self.presentViewController(vc, animated: true, completion: nil)
Final code for the firstViewController: