Create a new window in OS X app using menu bar - swift

I have made a pretty simple mac app with two windows. I am an iOS developer so I am very familiar with swift but a few of the UI elements in iOS apps don’t translate well when building OS X apps. I want to take advantage of the menu bar at the top of the screen. I want to click “New” under file which is there by default and open a new window of my initial VC. How do I do that?
I have read a bunch of posts and they have told me to build a new menu bar but I feel like there should be an easier way with the menu bar that is there by default. How do I call a specific VC even once I have managed to create an outlet or add an action for the new button? Can I just instantiate the VC like we would in iOS? I just want the easiest way to do this.
This is what I used to present the new window:
let storyboard = NSStoryboard(name: NSStoryboard.Name(rawValue: "Main"), bundle: nil)
let myWindowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier(rawValue: "LoginVC")) as! LoginViewController
if let window = NSApplication.shared.mainWindow
window.contentViewController = myWindowController //as! NSWindowController // just swap
}

By default the "New" menu item will send the newDocument(_:) message to the "first responder". If any object along the responder chain implements it, then the menu item will be enabled, otherwise the menu item is disabled.
To respond to this message you could either:
implement func newDocument(_ sender: Any?) somewhere in the responder chain (probably somewhere towards the end of it, like in the app delegate). In a document-based application the NSDocumentController would already handle this.
set your own action and target for the "New" menu item to call another method on a specific object to have it create and display the new window. This would not work for actions that are contextual based on where in the application the user is (for example Copy or Paste).

Related

How to create a customized dock menu for my macOS application in the dock using Xcode

I am trying to create a customized dock menu for my swift application in the dock so that when the user right click on the dock tile of my app after it has been launched, it will show my customized item in addition to the default dock menu items. I prefer to do it programmatically or creating the (static) menu item using Xcode.
I have reviewed similar questions posted here (How can I add a menu to the application in the dock?) and (Adding Items to the Dock Menu from my View Controller in my Cocoa App) but both referred to the old nib interface instead of how to create one using storyboard. I also reviewed the file template library in storyboard but couldn't find a template for dock menu (I only see one for Main Menu).
Any pointer on how to achieve this using storyboard or programmatically with swift will be much appreciated.
EDIT 4/24/2020: Since Ken's response, I have decided to customize the dock menu programmatically. Here is how I implemented applicationDockMenu(_:) in AppDelegate
EDIT #2 4/24/2020: fixed missing _ input argument to method and problem solved.
func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {
let myMenu = NSMenu(title: "MyMenu")
let myMenuItem = NSMenuItem(title: "My Item 1", action: nil, keyEquivalent: "")
myMenu.addItem(myMenuItem)
return myMenu
}
I must be missing something else since the new item did not show up with I right-clicked on the app's dock icon after the app is launched.
Any idea?
Thanks.
Kenny
You can implement the method applicationDockMenu(_:) in your app delegate class to return a menu of items to be added to the Dock menu.
If you check the docs for that method, it also discusses other methods for providing a static Dock menu. One of them involves creating a separate NIB. If there isn't a template for a menu NIB, you can simply create an empty one and add a menu object to it. That NIB is referenced by a key in the Info.plist file and doesn't need to involve your storyboard.

How to make two windows pop out in the beginning on a Swift 4 Cocoa application?

I'm working on a Swift 4 Cocoa Mac OS X (not iOS) project with XCode 9. I have two NSWindows in my main storyboard with different (subclasses of) NSViews. Currently only one window holds the storyboard entry point and so the other one does not appear when the application begins, and I want both windows to appear when the application is loaded.
I've tried googiling with different keywords but so far couldn't find a way. The only method I found was to connect a segue from a button or menu in one window to the other window, so that the other window appear whenever the button is pressed. Is there any of making both windows appear in the beginning the 'right way' (preferablly using functionalities of XCode storyboard)?
You have to get a reference to the window controller in your storyboard from the second window you want to show. Add this code to your NSApplicationDelegate in applicationDidFinishLaunching. Don't forget to set the identifier from the WindowController in the Storyboard
let storyboard = NSStoryboard(name: "Main", bundle: nil)
let windowController = storyboard.instantiateController(withIdentifier: "MyWindowController") as! NSWindowController
windowController.showWindow(self)

How do I open another window in macOS in Swift with Cocoa

I am working on a macOS app that presents a list of customer master records in a table view. Double-clicking a row in the table view should open a new window where the user can view and edit the customer information.
This is an Xcode 8.3.3 project using a storyboard and Swift.
It is not a document or core data app.
I have the main window working up to the point where the table view is displaying the records correctly and the associated view controller is receiving the double-click events and logging them to the console.
I have created an additional window controller and view for the edit window and verified its basic functionality by temporarily marking it as the initial controller.
What I haven't been able to figure out is how to display a new instance of that window when the user double-clicks a row.
Thanks to #Joshua Nozzi I'm closer. Here is the code at this point.
let storyboard = NSStoryboard(name: "Main", bundle: nil)
if let windowController = storyboard.instantiateController(withIdentifier: "xyzzy") as? NSWindowController
{
windowController.showWindow(self)
}
It's generating a
(Storyboard: 0x620000000680) doesn't contain a controller with
identifier 'xyzzy'
error.
The Window Programming Guide is a great place to understand how windows are managed in general.
Specifically (assuming you know how to present a window controller scene in storyboards), you need somewhere to store references to the new window controllers so they’re not immediately deallocated (and disappear) when presented.
In your case, you may want to keep an array of your open detail windows in the master window controller, so that if the master goes away, the details do as well. When a detail window is open (a controller instance is created and its window shown), you’ll store its controller in the array; when closed, you remove its controller from the array so it’s deallocated.
There are a number of ways to do this, depending on how much control you want, how you want child window ownership to work, etc., but this basic pattern is usually sufficient.
To instantiate a new window controller scene from a storyboard:
var myWindowController = NSStoryboard(name: "MyStoryboardFileName", bundle: nil)?.instantiateControllerWithIdentifier("MyWindowControllerIdentifier") as MyWindowControllerClass
myWindowController?.showWindow(self)
Additionally, to open new window, this code can help you
windowController.contentViewController = tabViewController
The full code is like that i used it in my project:
#objc func openApplicationView(_ sender: Any?) {
let mainStoryBoard = NSStoryboard(name: "Main", bundle: nil)
let tabViewController = mainStoryBoard.instantiateController(withIdentifier: "tabView") as? TabViewController
let windowController = mainStoryBoard.instantiateController(withIdentifier: "secondWindow") as! TabViewWindowController
windowController.showWindow(self)
windowController.contentViewController = tabViewController
}
It can helpful if you've closed the mainWindow. So you need to add one windowController and tabViewController(you can use normal view controller) in your own underlying storyboard.
In my side the tabViewController has been extended by NSTabViewController and tab view component has been bound with this class.
Note: I've also added the windowController in my Main.storyboard as a component and identified to use then on coding side.

Swift: How to prevent window to show up on launch?

I am writing a macOS Cocoa application with Swift and I don't know how to prevent, that the main window shows up on application startup. I want to check first, if there is a new version available on the server: if there is one, a spash screen should appear. I don't know how to accomplish this and couldn't find good information too. I also don't know how to get rid of the windows standard buttons like for closing the window. Any help?
Check settings of your main window in Xcode. There are some checkboxes which control window buttons and other settings in Attributes inspector.
You can disable or enable them programmatically when you accessing Window object.
For example you can implement this approach in WindowController like this:
class WindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
window!.styleMask &= ~NSClosableWindowMask
}
}
Previously you need to add your custom WindowController class to window controller object in storyboard (Identity inspector tab).

Loading Swift Main Storyboard and View Controller from Framework

I would like to have my Swift Project load it's main Storyboard and View Controller from a Framework rather than the project itself.
Is this possible?
All help welcome.
I'm sure you have long since resolved your issue, but posting the answer here in case it's useful to someone else coming across it from Google.
You can specify another storyboard as the main storyboard by selecting your project from the navigator, selecting the app target from the left panel and going into the 'Info' tab. There is a setting there for "Main storyboard base file name."
For your specific issue, that's probably not what you want though. Instead, open your app's Main.storyboard, highlight the view controller, open the Identity Inspector, and set the Class to the one from your framework. To get this to work for my project, I actually had to delete the existing Main.storyboard, create a new one, drop a View Controller on it, and set "Is Initial View Controller" from the Attributes Inspector first.
Updated answer :
Let's assume you have a framework called myLogin with a storyboard with id "frameworkStoryboard" and a view controller with id "login".
You would
import myLogin
to load your framework and then put in your code
let storyBoard = NSStoryboard(name: "frameworkStoryboard", bundle: nil) as NSStoryboard
let loginViewController = storyBoard.instantiateControllerWithIdentifier("login") as! NSViewController
view.window?.contentViewController = loginViewController
to load your storyboard and corresponding view.
Basically you could use variables for both names (storyboard and controller) instead of just writing it down.
The example is for osx, the only difference for iOS would be using UIViewController instead of NSViewController, same goes for the storyboard, also you would not present the controller the same way on iOS (this example will switch the current windows content view instead of presenting it.