Show/Hide Window by Clicking button in Swift - macos

I want to show/hide a window in swift by clicking a button from main window. Beginsheet is showing the window, but endsheet is not closing the window. My appdelegate code is given:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
var settingsController: SettingsController?
#IBAction func inSettings(sender: NSObject?)
{
settingsController = SettingsController(windowNibName: "SettingsController")
window.beginSheet(settingsController!.window!, completionHandler: nil)
}
#IBAction func outSettings(sender: NSObject?)
{
window.endSheet(settingsController!.window!)
}
}
SettingsController:
import Cocoa
class SettingsController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
}

Swift 3 Solution:
Lets say that you have WindowA and WindowB. You want to open WindowB but first You want to hide WindowA.
Connect windows with a segue. (Select "Show" as segues "Kind" property) And you need a static class to keep hidden window. in WindowA override shouldPerformSegue and keep WindowA as a static NSWindow object.
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
YourStaticClass.WindowA = self.view.window
self.view.window?.orderOut(self)
return true
}
orderOut(self) hides the window. Then WindowB will be opened.
In WindowB's view controller use a function to close windowB and show hidden WindowA:
#IBAction func btnBack_Click(_ sender: NSButton) {
YourStaticClass.WindowA?.makeKeyAndOrderFront(YourStaticClass.WindowA)
self.view.window?.close()
}

Use endSheet to end a document-modal sheet session. Like this:
#IBAction func outSettings(sender: NSObject?)
{
settingsController!.window!.endSheet(settingsController!.window!)
}
EDIT: You need to actually close the window in your completion handler you call orderOut, like this:
#IBAction func inSettings(sender: NSObject?)
{
settingsController = SettingsController(windowNibName: "SettingsController")
window.beginSheet(settingsController!.window!) {
settingsController!.window!.orderOut(nil)
}
}

Related

PerformSegueWithIdentifier not making the transition

So I am trying to perform a segue as soon as the window loads without having to click a button or have any action performed. The segue's are linked from a view controller, but not linked from the window controller because I can only get one segue to link from the Windowcontroller at a time. I tried this code in both "windowWillLoad" and "windowDidLoad" directly, and with it's own function. It isn't making the transition. Does it always have to be activated with a button? Can I not use segue's that aren't linked directly to the WindowController itself? I basically want to be able to choose between two different view controllers on the launch of the cocoa mac app.
import Cocoa
class WindowOne: NSWindowController {
var i = 0
override func windowWillLoad() {
}
override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject?) {
if i == 0 {
performSegueWithIdentifier("segone", sender: self)
} else {
performSegueWithIdentifier("segtwo", sender: self)
}
}
override func windowDidLoad() {
super.windowDidLoad()
}
}
The problem here is that the segue cannot be performed until the window has actually appeared - and at the point that windowDidLoad gets called it hasn't.
In iOS land we would handle this by calling performSegueWithIdentifier in the viewDidAppear method.
Here there are two options, as per this answer: viewWillAppear or viewDidAppear on NSWindowController
Either override the showWindow method:
import AppKit
class WindowOne: NSWindowController {
var i = 1
override func showWindow(_ sender: Any?) {
super.showWindow(sender)
if i == 0 {
performSegueWithIdentifier("segone", sender: self)
} else {
performSegueWithIdentifier("segtwo", sender: self)
}
}
}
Or for more fine-grained behaviour one might catch NSWindowDelegate calls. For example if you wanted the segue to occur whenever the window comes to the foreground you might implement windowDidBecomeKey:
import AppKit
class WindowOne: NSWindowController, NSWindowDelegate {
var i = 1
override func windowDidLoad() {
super.windowDidLoad()
self.window?.delegate = self
}
func windowDidBecomeKey(_ notification: Notification) {
if i == 0 {
performSegueWithIdentifier("segone", sender: self)
} else {
performSegueWithIdentifier("segtwo", sender: self)
}
}
}

Replace NSViewController under Swift2 Storyboard MAC OSX

I am new to Mac OSX and with Apple promoting the fact that the bodies of code are becoming similar decided to tell the folk I am writing code for we should be able to do a Mac OSX version. iPhone and iPad versions are all good and about to release second version so no issues there.
So I am subclassing NSWindowController to get access to the Toolbar and worked out how to remove and add items on the toolbar, but for the life of me I can not get one NSViewController (firstViewController) to dismiss and bring up the second NSViewController (secondViewController) in the same NSWindowController.
So the 2 issues are that
1. I want to be able to performSegueWithIdentifier from the first NSViewController in code and
2. bring up the second NSViewController by replacing the first NSViewController in the same NSWindowController.
If I add a button to the firstViewController and put a segue to the secondViewController then when I select the button the secondViewController comes up just fine but in a seperate window not the same NSWindowController that I want it to and the firstViewController does not get replaced but stays in the NSWindowController.
So I know the segue idea will work but its not working in code and when I do insert the segue from a button it works but into a seperate NSViewController that is not part of the NSWindowController.
I am trying to find some programming guide from Apple on the issue but no luck so far.
Here is an overview from my Storyboard:
Here is my NSWindowController subclassed and the func loginToMe2Team is trigger from the NSToolBar and its working just find as the print statements show up on the console.
import Cocoa
class me2teamWindowsController: NSWindowController {
#IBOutlet var mySignUp : NSToolbarItem!
#IBOutlet var myToolbar : NSToolbar!
let controller = ViewController()
override func windowDidLoad() {
super.windowDidLoad()
print("window loaded")
}
override func windowWillLoad() {
print("window will load")
}
#IBAction func logInToMe2Team(sender: AnyObject){
controller.LogIn() //THIS IS THE FUNC I AM TESTING WITH
}
#IBAction func signUpToMe2Team(sender: AnyObject){
controller.signUp()
}
Here is my NSViewController subclassed with the func LogIn. Its getting selected just fine but the performSegueWithIdentifier is not. And I did cut and past the Identifier to make absolutely sure it was the same.
import Cocoa
import WebKit
class ViewController: NSViewController {
#IBOutlet weak var theWebPage: WebView!
#IBOutlet weak var progressIndicator: NSProgressIndicator!
override func viewDidLoad() {
super.viewDidLoad()
let urlString = "https://thewebpage.com.au"
self.theWebPage.mainFrame.loadRequest(NSURLRequest(URL: NSURL(string: urlString)!))
}
override func viewDidAppear() {
}
func LogIn() {
print("I logged in")
self.performSegueWithIdentifier("goToTeamPage", sender: self)
//THIS IS THE BIT THATS NOT WORKING
}
func signUp() {
print("I have to sign up now")
}
override var representedObject: AnyObject? {
didSet {
}
}
func webView(sender: WebView!, didStartProvisionalLoadForFrame frame: WebFrame!)
{
self.progressIndicator.startAnimation(self)
}
func webView(sender: WebView!, didFinishLoadForFrame frame: WebFrame!)
{
self.progressIndicator.stopAnimation(self)
}
}
You need to use a custom segue class (or possibly NSTabViewController if it’s enough for your needs). Set the segue’s type to Custom, with your class name specified:
…and implement it. With no animation, it’s simple:
class ReplaceSegue: NSStoryboardSegue {
override func perform() {
if let src = self.sourceController as? NSViewController,
let dest = self.destinationController as? NSViewController,
let window = src.view.window {
// this updates the content and adjusts window size
window.contentViewController = dest
}
}
}
In my case, I was using a sheet and wanted to transition to a different sheet with a different size, so I needed to do more:
class ReplaceSheetSegue: NSStoryboardSegue {
override func perform() {
if let src = self.sourceController as? NSViewController,
let dest = self.destinationController as? NSViewController,
let window = src.view.window {
// calculate new frame:
var rect = window.frameRectForContentRect(dest.view.frame)
rect.origin.x += (src.view.frame.width - dest.view.frame.width) / 2
rect.origin.y += src.view.frame.height - dest.view.frame.height
// don’t shrink visible content, prevent minsize from intervening:
window.contentViewController = nil
// animate resizing (TODO: crossover blending):
window.setFrame(window.convertRectToScreen(rect), display: true, animate: true)
// set new controller
window.contentViewController = dest
}
}
}

Nib's custom class won't hook up to class

The nib I am loading is a custom About window for my app. When the 'About' NSMenuItem is pressed I load the nib in AppDelegate in the following manner:
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var about = NSWindowController()
#IBAction func aboutClicked(sender: NSMenuItem) {
about = AboutWindow(windowNibName: "AboutWindow") as AboutWindow
NSBundle.mainBundle().loadNibNamed("AboutWindow", owner: about, topLevelObjects: nil)
}
*both the class that I want to hook up to the nib and nib itself are named 'AboutWindow'.
Once I create my NSWindowController and it's nib file, The custom class option for the nib's NSWindow does not allow me to put in the 'AboutWindow' class that I created alongside the nib.
As you can see the nib's custom class is set to NSWindow and it won't change
Any help on how to hook up this custom class and the nib is greatly appreciated.
Can you describe what the following doesn't do that you want to be done:
//
// AboutWindowController.swift
// AboutWindow
//
import Cocoa
class AboutWindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
}
}
That was hooked up automatically by Xcode to AboutWindowController.xib, whose name I changed to AboutWindow.xib.
Next file:
//
// AppDelegate.swift
// AboutWindow
//
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
var about = NSWindowController()
func applicationDidFinishLaunching(aNotification: NSNotification) {
// Insert code here to initialize your application
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
#IBAction func aboutClicked(sender: NSMenuItem) {
about = AboutWindowController(windowNibName: "AboutWindow")
about.showWindow(self)
}
}
In IB, I dragged from the About menu item to the First Responder object to hook up the action. When I run the app and click on the About menu item, the AboutWindow displays.
I did not attempt to change any class names in IB.
Response to comment:
If I change AboutWindowController to this:
//
// AboutWindowController.swift
// AboutWindow
//
import Cocoa
class AboutWindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
// Implement this method to handle any initialization after your window controller's window has been loaded from its nib file.
println("The About window has loaded")
}
}
I see the message printed in the console.

Trigger Segue Artificially

I am trying to pass data between two viewContollers in an OS X storyboard application using Swift. When I press a button on VC1, it opens VC2, and prepareForSegue is run. However, I can't pass data back to VC1 because a. prepareForSegue isn't being run (because a window isn't being opened) and b. because even if it were, VC1 doesn't know data is being sent and I can't figure out a function (something like viewDidBecomeFocus, if such a function existed) to let it know to look. I feel like there must be a way to do this.
If you know of a way to do this in IOS but not OSX, it could still be useful.
Thanks!
Let assume that in your first ViewController you have one label and one button. When pressed, that button open popover (SecondViewController) with one textfield (and one button what says ready or close etc.), where you want take its value and assign it to your label. That is where delegates and protocols come handy.
SecondViewController:
#objc protocol TextDelegate {
func passedString(textValue: String)
}
class SecondViewController: NSViewController {
#IBOutlet weak var textField: NSTextField!
weak var delegate: TextDelegate?
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
#IBAction func closePopOver(sender: AnyObject) {
if delegate != nil {
delegate!.passedString(textField.stringValue)
}
self.dismissViewController(self)
}
}
This is ViewController:
#IBOutlet weak var myLabel: NSTextField!
override func prepareForSegue(segue: NSStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "mySegue" {
let vc = segue.destinationController as! SecondViewController
vc.delegate = self
}
}
func passedString(textValue: String) {
myLabel.stringValue = textValue
}

OS X storyboard: how to show a window programmatically?

I am creating an OS X status bar application.
I am trying to achieve the following:
app starts invisible, with menu bar item
click on menu bar item shows the main window
on deactivate, the window is hidden
So I am trying to programmatically show the main window when the menu item is clicked, but with no success.
My main window has "Hide on deactivate" checked. Once hidden, I cannot make it visible again using code.
Here is the code I have for now, but it doesn't work:
#IBAction func menuClick(sender: AnyObject) {
var mainWindow = NSStoryboard(name: "Main", bundle: nil)?.instantiateInitialController()
mainWindow?.makeKeyAndOrderFront(self)
}
This is how you have to do to show your Windows programmatically:
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
let mainWindow = NSWindow(contentRect: NSMakeRect(0, 0, NSScreen.mainScreen()!.frame.width/2, NSScreen.mainScreen()!.frame.height/2), styleMask: NSTitledWindowMask|NSResizableWindowMask|NSMiniaturizableWindowMask|NSClosableWindowMask, backing: NSBackingStoreType.Buffered, defer: false)
func createNewWindow(){
mainWindow.title = "Main Window"
mainWindow.opaque = false
mainWindow.center()
mainWindow.hidesOnDeactivate = true
mainWindow.movableByWindowBackground = true
mainWindow.backgroundColor = NSColor(calibratedHue: 0, saturation: 0, brightness: 1, alpha: 1)
mainWindow.makeKeyAndOrderFront(nil)
}
func applicationDidFinishLaunching(aNotification: NSNotification) {
// lets get rid of the main window just closing it as soon as the app launches
NSApplication.sharedApplication().windows.first!.close()
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
#IBAction func menuClick(sender: AnyObject) {
createNewWindow()
}
}
or you can create an optional NSWindow var to store your window before you close it as follow
import Cocoa
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
var defaultWindow:NSWindow?
func applicationDidFinishLaunching(aNotification: NSNotification) {
// lets get rid of the main window just closing it as soon as the app launches
defaultWindow = NSApplication.sharedApplication().windows.first as? NSWindow
if let defaultWindow = defaultWindow {
defaultWindow.close()
}
}
func applicationWillTerminate(aNotification: NSNotification) {
// Insert code here to tear down your application
}
#IBAction func menuClick(sender: AnyObject) {
if let defaultWindow = defaultWindow {
defaultWindow.makeKeyAndOrderFront(nil)
}
}
}
The makeKeyAndOrderFront method is a NSWindow method, but instantiateInitialController returns the window controller, not its window.
Also, if the window is hidden on deactivate, you wouldn't want to instantiate another copy. Keep a reference to the window and re-show that.
Finally, you may need to bring the app to the front too. Call [NSApp activateIgnoringOtherApps:YES] (or the Swift equivalent).

Resources