Relaunch view when app is closed - swift

I am creating a status bar app for Mac with a settings view.
I have created a NSMenuItem to launch the settings but I don't find any solutions to launch this view.
What I have tried:
NSWorkspace.shared().launchApplication("AppName")
and
StatusMenuController.swift
func showSettings() {
var mainWindow: MainWindowController!
mainWindow = MainWindowController()
mainWindow.showWindow(nil)
}
MainWindowController.swift
override func windowDidLoad() {
super.windowDidLoad()
self.window?.center()
self.window?.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
}

I saw this problem was faced in many cases and I found a solution that I don't completely understand but that I'll try to explain.
Here is how documents are managed in macOS :
From my experience, you need to check the box "Create Document-Based Application" when creating a new Mac app to have a NSDocument class (You can also add it later) which will handle how your views are being showed or not.
Adding this NSDocument class to my project made the following code work (and wasn't before) :
let storyboard = NSStoryboard(name: "Main", bundle: nil)
var windowController: NSWindowController!
windowController = storyboard.instantiateController(withIdentifier: "ScanWindowController") as! NSWindowController
windowController.showWindow(nil)

Related

Document based app using one single window

I have a document-based macOS application, which is a basic text editor. The default behavior is to create a new window for every opened document. But I want to only have one window displayed at a time and when opening a document or creating a new one, this should happen in the same window and thus replace the old document.
I've tried to do some hacking in the makeWindowControllers method from NSDocument by not instantiating a new window controller but reusing an old one. But after some problems with this approach, I figured this is not the right way to go. I was wondering if there is a common approach to this problem.
This is the code I've tried
class Document: NSDocument {
static var sharedWindow: NSWindowController?
override func makeWindowControllers() {
// Instaniate window controller there is none
if Document.sharedWindow == nil {
// Returns the Storyboard that contains your Document window.
let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
Document.sharedWindow = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as? NSWindowController
}
guard let sharedWindow = Document.sharedWindow else { return }
if let previousDocument = sharedWindow.document as? NSDocument {
previousDocument.close()
}
self.addWindowController(sharedWindow)
sharedWindow.contentViewController?.representedObject = content
(sharedWindow.contentViewController as? ViewController)?.handleOpenDocumentOperation()
}
...
}

Swift: [NSNib _initWithNibNamed:bundle:options:] could not load the nibName

I am building a Cocoa app for production and when I create an NSViewController for routing without NSStoryboard instantiate, I got the error like below.
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[NSNib _initWithNibNamed:bundle:options:] could not load the nibName: ContentFully.AnotherController in bundle (null).
Actually I solved my problem via using an NSViewController to adding NSStoryboard but I would like to learn what is going on when I call it programmatically and why did crash?
Working scenario
let storyboard = NSStoryboard(name: "Dashboard", bundle: nil)
let vc = storyboard.instantiateInitialController() as! AnotherController
Crashed scenario
let vc = AnotherController()
self.view.window?.contentViewController = vc
Even if I create a class fully programmatically and non-relational with NSStoryboard I could not use it when I change the contentViewController. Is it necessary to search every controller in NSBundle for Swift?
Thanks in advance
A very simple working version would be:
import Cocoa
class AnotherController: NSViewController {
override func loadView() {
view = NSView(frame: NSMakeRect(0.0, 0.0, 400.0, 270.0))
let label = NSTextField(labelWithString: "Another Controller")
view.addSubview(label)
}
}
I can call that from my first controller using the code you say crashes and get the expected result.
#IBAction func replaceMe(_ sender: Any) {
let vc = AnotherController()
self.view.window?.contentViewController = vc
}

Create a Window on a status bar app for macOS

Warning: macOS dev beginner here.
I have a menu bar app (with no dock). Most of the app's functionality is in the menu (and implementation is in AppDelegate), but I need a separate window that will open once I click one of the menu items.
I want to use SwiftUI, Swift 5, Xcode 11.3.
I haven't found an appropriate way to do this. Which files and similar need to be created? How to open this window programatically?
#objc func openPreferences() {
// open a new window here...
}
You have to create a window programatically. I have attached sample code of one of my apps:
private var windowController: NSWindowController?
fileprivate func createWindow()
{
let storyboard = NSStoryboard(name: "Main", bundle: nil)
self.windowController = storyboard.instantiateInitialController() as? NSWindowController
// This is example code to show how to customize the hosted view controller. You can pass additional arguments here (may an important global variables that is declared in the AppDelegate).
if let contentController = windowController?.contentViewController as? MyWindowViewController
{
// Do some assignments here
// contentController.variable = ....
// self.windowViewController = contentController // Maybe save for later use.
}
}
#objc fileprivate func open()
{
if self.windowViewController == nil
{
self.createWindow()
}
self.windowController?.showWindow(self)
NSApp.activate(ignoringOtherApps: true) // Bring window to front.
}
I have linked the open() function to a button call (hence the #objc keyword). I think that you already did this, so my open() function would be your openPreferences function.

Storyboard doesn't contain a controller with identifier 'PlannerViewController'

I attached a PlannerViewController to my Main.storyboard. When I build and run the app, I get the following error :
Storyboard (<NSStoryboard: 0x608000001790>) doesn't contain a controller with identifier 'PlannerViewController'
In Main.storyboard, I did set PlannerViewController as the custom class and storyboard ID. Here's the code in PlannerViewController.swift :
import Cocoa
class PlannerViewController: NSViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do view setup here.
}
}
extension PlannerViewController {
// MARK: Storyboard instantiation
static func freshController() -> PlannerViewController {
let storyboard = NSStoryboard(name: "Main", bundle: nil)
guard let viewcontroller = storyboard.instantiateController(withIdentifier: "PlannerViewController") as? PlannerViewController else {
fatalError("Why cant i find QuotesViewController? - Check Main.storyboard")
}
return viewcontroller
}
}
Does anyone know how to solve this error ?
Please note that I'm using Swift 3, and that I'm new to Swift.
Thanks.
EDIT : By the way, I'm following this tutorial to develop a menu bar app.
You need to add id PlannerViewController here

How to restore window position in an OSX application?

I created a storyboard it has window view controller as initial view controller. I gave the window an autosave name preferencesWindow. In the preferences I checked [x] Restorable and [x] Release when closed.
When I go into the menu and click Preferences I load the window controller like so:
let storyboard = NSStoryboard(name: "Preferences", bundle: nil)
let windowController = storyboard.instantiateInitialController() as? NSWindowController
let window = windowController?.window
windowController!.showWindow(self)
This will present the preferences view controller and when I drag it to another position and click the close button it will close. So far so good. However when I load the window again from the menu, it shows on it's original position instead of the position I last dragged the window to. Why is this?
Answer
It appears to be a bug in xCode 7 setting the auto save name in code solved it.
let storyboard = NSStoryboard(name: "Preferences", bundle: nil)
let windowController = storyboard.instantiateInitialController() as? NSWindowController
let window = windowController?.window
window!.setFrameAutosaveName("preferences")
windowController!.showWindow(self)
This is a bug in Xcode 6 and I don't know if it is fixed in Xcode 7.
Setting autosave in InterfaceBuilder has no effect. To get it to work just set its name in windowDidLoad() of your windowController:
class MyWindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
self.windowFrameAutosaveName = "position"
}
}
Swift 4:
final class MyWindowController: NSWindowController {
override func windowDidLoad() {
super.windowDidLoad()
self.windowFrameAutosaveName = NSWindow.FrameAutosaveName(rawValue: "myWindow")
}
}