Display windows present in different storyboards - swift

I'm using Xcode 8 and storyboards for mac development.
I have 2 storyboards with NSWindowController in each of them.
1). How can I display both windows when the app is launched?
2). How can I display window on pressing button on other window?
Edit:
Code:
func applicationDidFinishLaunching(_ aNotification: Notification)
{
let st = NSStoryboard(name: "Logs", bundle: nil) // Logs is my storyboard name
var logWindow: NSWindowController? = nil
logWindow = st.instantiateInitialController() as! NSWindowController?
if logWindow?.window?.isVisible == false
{
logWindow?.window?.setIsVisible(true)
}
}

Everything is correct but the problem is with logWindow.
I assume, you know that the local variable is dead after the execution of method.
As you can see logWindow is a local variable. When applicationDidFinishLaunching(_ aNotification: Notification) is executing, the variable is alive, a new window is created and also displayed.
When applicationDidFinishLaunching(_ aNotification: Notification) is finished executing, your variable logWindow is not alive. So, the allocated memory is de-allocated automatically (by ARC) and the window is destroyed/de-allocated.
All this is happening so fast and it is the reason you are not seeing the window inspite of isVisible() returning true(it returns true because the method is still in execution and logWindow is still alive).
So just make logWindow a variable of class(in what ever class you are trying to display the window) and you are good to go.

Both questions boil down to pretty much the same thing: How can I display a window from a storyboard? Whether you want to do it when the app is launched or when the user clicks a button shouldn't really affect what you need to do to display the window.
Take a look at the NSStoryboard class and you'll find methods for instantiating a window controller from a storyboard. So, create an instance of NSStoryboard if you don't already have one, and then use that to instantiate the window controller in question. For example, if your window controller is called "My Window Controller" and is located in the main storyboard...
let storyboard = NSStoryboard(name: "Main", bundle: nil)
let windowController = storyboard.instantiateController(withIdentifier: "My Window Controller") as! NSWindowController
Then do whatever you like with windowController. If you want to display two windows when the app is launched, then maybe your app delegate has two properties for keeping track of the corresponding window controllers. In your app delegate's applicationDidFinishLaunching(notification:) method, you could create two windows using code like I've shown and assign the window controllers to those windows. If you want to open a window when the user clicks a button, put code like the code above in the button's action, and store a reference to the resulting window controller in some appropriate object, possibly the window controller where the action appears.

Related

Black screen with viewDidLoad after if else statement

I want my app to check at start conditionaly if a variable is correct or not. Based on that it should either go to an intro screen (where he can select a variable in my case select a team) or it should start the main view. After searching i found this code and edited it. But there still seems to be problems. First of all I dont have two identifier. The Intro has one but not the main view. My main View is called WeatherViewController and the Intro screen is called FirstScreenViewController. I also added a picture of my Main.storyboard.
I also googled a lot about conditional UINavigationController but I can only understand with a video and did not found a video about it.
I tried to use the code from here.
var id = hello ? "goToIntro" : "???"
self.window = UIWindow(frame: UIScreen.main.bounds)
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let WeatherViewController: UIViewController = mainStoryboard.FirstScreenViewController(withIdentifier: WVC has no identifier??) as UIViewController
self.window?.rootViewController = WeatherViewController
self.window?.makeKeyAndVisible()
if hello {
self.performSegue(withIdentifier: "goToIntro", sender: self)
} else {
/here nothing should happen. It should open the Main View
self.performSegue(withIdentifier: "???", sender: self)
}
Note: This answer was referring to the original question (before any edits) that was attempting a segue inside loadView().
You're supposed to manually create your controller's view in loadView. You're currently doing nothing, hence the black screen. Furthermore, if you are using a Storyboard or a xib for managing this controller you shouldn't be overriding loadView at all.
Having said that it might be a better idea to move this branching logic a step back, to "something" (a container controller like a UINavigationController or a custom one, or even directly setting the root controller of your window if it makes sense) that will present (or set) A or B based on some condition and thus avoid to load this controller altogether (have in mind that in your code, the controller is initialized, it will be part of the hierarchy and all the lifecycle methods will be called even if you segued directly to another one)
Finally, if you decide for some reason to override loadView you don't have to call viewDidLoad yourself, the system will call this method.

How to override Copy and Paste NSMenuItems for one View Controller Swift macOS

I am writing a macOS application with multiple view controllers, using Storyboards.
In my main View Controller I would like to be able to copy and paste data to the NSPasteboard. The data is related to buttons displayed to the user, and the exact data to be copied varies depending on which button has most recently been pressed/selected.
I would like to be able to override the standard behaviour of the Copy and Paste NSMenuItems when my main View Controller is the front most (key) window, but revert to back to standard behaviour when other windows are in the foreground, as they all contain NSTextFields which can be copied/pasted into.
I have done a lot of googling, and overriding this behaviour is not very well documented. I can achieve it globally by adding an IBAction into the App Delegate, which I could use to call a function in whichever View Controller is key, but this doesn't feel like a very elegant solution.
Currently my IBAction in the App Delegate looks like this:
#IBAction func copy(_ sender: Any) {
if let window = NSApplication.shared.keyWindow {
if let splitView = window.contentViewController as? SplitViewController {
if let controlVC = splitView.controlItem.viewController as? ControlViewController {
controlVC.copyAction(self)
}
}
}
}
Am I missing a neater solution?
Thanks,
Dan

In Xcode, How Can I Add A Tutorial Window to My Mac App, Which Opens Only On First Launch

I'm designing a MacOS app, for which I'd like to have a second window appear on the first time the app is launched (similar to what Apple does on the first time you open iMovie or Pages). I've tried a couple things to no avail. So far, I've created a second group as a target of the original app. The new group contains the tutorial storyboard I designed and a custom class called OnboardingWindowController (a subclass of NSWindowController). I gave the window controller the Storyboard ID of OnboardingWindowController, and added the following method:
class func loadFromNib() -> OnboardingWindowController {
let vc = NSStoryboard(name: "Onboarding", bundle: nil).instantiateController(withIdentifier: "OnboardingWindowController") as! OnboardingWindowController
return vc
}
I'm not sure, however, how to get this to launch without prompting the first time the app is launched and then never again. Thanks in advance!
Add a key to userdefaults check it's value, if it's false then show the tutorial else don't.
if UserDefaults.standard.bool(forKey: "didShowTutorial") {
// Code to proceed without showing tutorial
} else {
UserDefaults.standard.set(true, forKey: "didShowTutorial")
// Code to show the tutorial
}

message sent to deallocated instance 0x600000147e80 error by clicking sub view event in container view OS X Swift

I am new in os x programming. I am using swift to developing my mac app. In my app home screen contains a Container view and some NSButtons. By clicking on the buttons the NSViewControllers (created in storyboard) appear on the ContainerView. This is done by the code below:
self.ContainerView.subviews.removeAll()
let myViewController = iR_DELEGATE.GoToPage("Questions",container: ContainerView)
self.ContainerView.addSubview(myViewController.view)
the GoToPage function code is:
func GoToPage(identify: String,container: NSView) -> NSViewController
{
let storyBoard = NSStoryboard(name: "Main", bundle: nil) as NSStoryboard
let myViewController = storyBoard.instantiateControllerWithIdentifier(identify) as! NSViewController
myViewController.view .setFrameSize(CGSize.init(width: container.frame.width, height: container.frame.height))
return myViewController
}
The problem is when I click on the button or select the table's view in the sub view controller that displayed in container view the app crash with error:
message sent to deallocated instance 0x600000147e80
I got this error by enable zombie objects.
Your crash is likely caused by the fact that the view controller is deallocated almost immediately after you created it.
A view controller holds a reference to its view, but not the other way around. When you add myViewController.view as a subview, it is retained and displayed. In the meantime, myViewController itself is not retained by anything, so it is removed from memory after the variable is last used.
You probably have your view controller as target to button's action. When you tap the button, it wants to send the action to the view controller, but it has been already deallocated, and it crashes.
To solve it, you need to keep the reference to myViewController to keep it from being deallocated earlier than needed. Either just store it in a variable, or consider using child view controllers for that purpose: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSViewController_Class/#//apple_ref/doc/uid/TP40005253-CH1-SW34

Come back to the tabBarController, swift

Currently on my viewController : Upload, my button send the data to my database only if all the information are filled out, and I come back to the preview view (table View) with :
self.navigationController?.popViewControllerAnimated(true)
I would like, if it is possible, to come back to my main view on the tabBarController. I tried many things, like directly on the storyboard with Present modally segue to "TabBar controller", but I come back to the TabBar without sending my data to the database and without checking in the information are filled out..
How can I do it?
Thanks!
UITabBarController has a property selectedIndex with which you can switch the selected tab. So on completion after dismissing the UploadViewController you can run:
self.tabBarController?.selectedIndex = 0 // Index to select
It would probably be best to create a delegate for your UploadViewController to fire a function to do all the work in your previewVC on API call completion.
(Super late response...in case someone has similar questions, presumably in later version of Swift, such as mine which is Swift 5, iOS 13.2). Steps:
Be sure to set an id for your UITabBarController storyboard, e.g. "TabBarViewController"
Next, add the following to an action that has already been connected to a button:
let ID_TABBAR = "TabBarViewCOntroller"
#IBAction func returnToTabbar(_ sender: Any) {
let tabBarController = self.storyboard?.instantiateViewController(identifier:ID_TABBAR) as! UITabBarController
self.navigationController?.pushViewController(tabBarController, animated:true)
}
Referenced from one of the responses from this post.
Update: In case your Tab Bar View Controller also happens to be the root view controller, the two lines of the code in the returnToTabbar method above can be:
self.dismiss(animated:true, completion:nil);
self.navigationController?.popViewController(animated:true);
(ref.: See answer here, for Swift4 but works just fine in Swift5)