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

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
}

Related

Going from one storyboard to another from a tableView in Swift 5 / Cocoa

have search on that topic without finding a solution that work.
I am building a accounting application with several storyboard. Main, Customer( clients), invoice (factures)... etc. I can go from the main storyboard to the customer of Invoice storyboard by click a button no problem... The button (main SB) is linked to the Customer or Invoice storyboard reference.
In the clients storyboard, I have a tableView with that list the purchased historic of that customer. I would like to to be able to double clic on a specific invoice, and open that invoice in the Invoice storyboard.
The double clic part work fine, print message work... but the program crash after with the message: Could not cast value of type '__NSCFBoolean' (0x7fffaab000c8) to '__C.NSViewControllerPresentationAnimator'
That code was taken andadapted from another post. I have tried different variation withou success ie same error message.
I have not work on the part where I transfer the Invoice number from the client SB to the Invoice SB. I will likely transfer the Invoice number with a segue and have the Invoices program look if that variable if not nil, after loading
Invoice storyboard filename : factures.storyboard
facture ViewController Class : FacturesVC
ViewController storyboardID : facturesVC_id
#objc func tableViewDoubleClick(_ sender:AnyObject) {
if tableView.selectedRow >= 0 {
print ("VC545:", tableView.selectedRow)
//let storyboard = NSStoryboard(name: "factures", bundle: nil)
//let VC = storyboard.instantiateViewController(withIdentifier: "facturesVC_id") // give same error
let VC = NSStoryboard(name: "factures", bundle: nil).instantiateController(withIdentifier: "facturesVC_id") as! FacturesVC
self.present(VC as NSViewController, animator: true as! NSViewControllerPresentationAnimator)
}
}
Your code does not make sense.
It looks like you are trying to call present(_:animator:). If you call that, you need to pass it an animator (an object of type NSViewControllerPresentationAnimator.)
Your code does not create a NSViewControllerPresentationAnimator.
Here is an outline of how you need to change it:
let vc = NSStoryboard(name: "factures", bundle: nil).instantiateController(withIdentifier: "facturesVC_id") as! FacturesVC
let animator = // Code to create an NSViewControllerPresentationAnimator
self.present(vc, animator: animator)
I haven't worked with NSViewControllerPresentationAnimators before. (I mostly work with iOS these days.) You should probably search for tutorials on NSViewControllerPresentationAnimator if you are unsure how to proceed.
Finally, I have found the answer I was looking for...
Here is the code.
#objc func tableViewDoubleClick(_ sender:AnyObject) {
if tableView.selectedRow >= 0 {
let srow = tableView.selectedRow
//print ("VC551:", srow)
fact_nb = Int(fact_tbv[srow].id_f) ?? 0 // invoice nb that you want to segue
let storyboard = NSStoryboard(name: "factures", bundle: nil)
let VC = storyboard.instantiateController(withIdentifier: "facturesVC_id")
//self.presentAsSheet(VC as! NSViewController) work fine for sheet
// self.presentingViewController // data are laoded but nothing show up
// self.presentAsModalWindow(VC as! NSViewController) // OK for modal, cannot be resize , yellow button missing on bar
// self.present(VC as! NSViewController, animator: false as! NSViewControllerPresentationAnimator) // true or false... need a animator
let window = NSWindow(contentViewController: VC as! NSViewController)
window.center()
let windowController = NSWindowController(window: window)
windowController.showWindow(nil)
//see How to Perform Segue https://www.youtube.com/watch?v=JL0xuZ4TXrM
self.performSegue(withIdentifier: "gotofact", sender: nil) // segue identifier name : gotofact
}
}
override func prepare(for segue: NSStoryboardSegue, sender: Any?) {
let sb = segue.destinationController as! FacturesVC
print ("VC569:", fact_nb)
sb.factnb = fact_nb
}

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

Quicklook always displays "no file to preview" error (url is valid)

I'm trying to use QuickLookController subclass as a child controller, setting its view as a subview in the parent. However, it always displays "no file to preview" message in the opening window. URL in the data source is valid, but the controller is never trying to get it! func previewItemAt index is never invoked!
func "numberOfPreviewItems" invokes always.
Please, help!
I get it. driven by example in article https://williamboles.me/hosting-viewcontrollers-in-cells/ I loaded my controller from bundle:
static func createFromStoryBoard() -> PreviewControler {
let storyboard = UIStoryboard(name: "PreviewControler", bundle: Bundle(for: PreviewControler.self))
guard let viewController = storyboard.instantiateViewController(withIdentifier: "PreviewControler") as? PreviewControler else {
fatalError("PreviewControler should be present in storyboard")
}
return viewController
}
But QuickLook controller must be created with it's constructor, so change to
let viewController = PreviewController()
solved the problem. Now all is fine.

All ViewController Views are Nil in Unit Testing?

Sorry if this is a beginner question but I'm relatively new to Unit testing and didn't see this asked anywhere.
When I start my unit tests in Swift, I setUp my tests by instantiating my viewController.
My code is set up using MVVM (Model - View - ViewModel). So when I test some of my viewModel methods, they will update the Views (in the ViewController) in the UI. The problem is, Xcode keeps crashing and says that the views in the ViewController are nil? How do I prevent these views from being nil? Am I doing something wrong? How do I instantiate the views within the viewController? I thought this would be automatic.
class WeirdFaceTests: XCTestCase {
var viewController: ViewController?
var tattooModel: ARModel?
var tattooViewModel: ARViewModel?
var mainUIModel: MainUIModel?
var mainUIViewModel: MainUIViewModel?
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
super.setUp()
self.viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PrimaryViewController") as! ViewController
self.tattooModel = ARModel(imageName: "blank", tattooType: .new)
self.tattooViewModel = ARViewModel(tattooModel: tattooModel!, delegate: viewController!)
self.mainUIModel = MainUIModel()
self.mainUIViewModel = MainUIViewModel(model: mainUIModel!, delegate: viewController!)
}
Turns out I was missing the self.viewController?.loadView() call in the setUp() method.
The correct code that works without nil views is as follows:
class WeirdFaceTests: XCTestCase {
var viewController: ViewController?
var tattooModel: ARModel?
var tattooViewModel: ARViewModel?
var mainUIModel: MainUIModel?
var mainUIViewModel: MainUIViewModel?
override func setUp() {
// Put setup code here. This method is called before the invocation of each test method in the class.
super.setUp()
self.viewController = UIStoryboard(name: "Main", bundle:nil).instantiateViewController(withIdentifier: "PrimaryViewController") as! ViewController
self.viewController?.loadView()

Relaunch view when app is closed

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)