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

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.

Related

how to set class to viewcontroller programmatically in swift?

i have 3 buttons, 3 Classes and 3 Viewcontrollers same UI, only different values.
so i want to do 3 buttons showed 1 Viewcontrollers but used different Classes.
My logic:
when pressing the button > set class > present vc
#IBAction func openTest(_ sender: Any) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ExSim")
// i want to change class here
present(vc, animated: true)
}
You have a basic misunderstanding. When you add a view controller to a storyboard, you tell the storyboard what subclass of view controller you are adding, and give it a unique identifier. Any time you instantiate a view controller from your storyboard using a specific identifier, you will always get a view controller of the subclass that’s defined in the storyboard. You can’t fetch a view controller from a storyboard and then transform it to a different class.
If you have 3 different screens that have have the same UI but different contents, you should use a model object to describe the contents that view controller displays. Always use the same identifier to instantiate it, and install a different model object into it depending on which button is pressed. Have the view controller load its contents into its views from the model object.
If you don’t know what a model object is, you need to read up on design patterns like MVC and MVVM, and maybe try a few tutorials that create apps using the MVC and MVVM design patterns

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)

Strange behaviour of Views

I'm designing an app for IOs in Swift 4.
Firs a login view appears and if you press the login button the following code will be executed:
let storyBoard : UIStoryboard = UIStoryboard(name: "TabBar", bundle:nil)
let resultViewController = storyBoard.instantiateViewController(withIdentifier: "TabBar") as! UITabBarController
resultViewController.modalTransitionStyle = .partialCurl
self.present(resultViewController, animated:true, completion:nil)
If we go to the any view, for example
you will see a table view with 3 sections, the content is static. When you press whenever (doesn't mind if its the cell to the tittle of the section) it goes back to the login page. This happens in all the pages linked at the TabBar when you Tapped the 2/3 first area (the 2 first sections more or less) of the screen.
It seems that it's executing:
dismiss(animated: true, completion: nil)
Why it is having that behaviour? How I can solve it?
This either seems to be a bug or expected behavior. Users have reported it from back in iOS 7.
It seem to me that apple has defaulted the animation to just view the content not interact with them.
Here is an answer discussing the same.
I would suggest you to make a custom logic.

Create a new window in OS X app using menu bar

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).

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.