How do I change viewControllers in swift with Foundation Xcode - swift

I am trying to programmatically switch to another viewController scene when a certain event happens in Xcode for a MacOS app but am having trouble.
The second view controller is in the same storyboard as the one I am trying to switch from.
I currently have
let secondViewController = ViewController(nibName: "outcome", bundle: nil)
self.present(secondViewController, animator: true as! NSViewControllerPresentationAnimator)
but it seems to crash when the event is triggered:
Could not cast value of type 'Swift.Bool' (0x2010043a8) to '__C.NSViewControllerPresentationAnimator' (0x2007787d0).
it also shows an error Thread 1: signal SIGABRT on the second command where I self.present

you are trying to cast a bool to NSViewControllerPresentationAnimator
try this code instead:
let secondViewController = ViewController(nibName: "outcome", bundle: nil)
self.present(secondViewController, animator: true)

Related

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
}

"Could not cast value of type" when try to pushViewController

I have two classes MenuVC, CategoryVC and CategorySubclassVC (which is a subclass of CategoryVC and I used it to fill data). I also have a Storyboard for CategoryVC with Storyboard ID categoryID.
In didSelectRowAt indexPath I tried to push view controller from MenuVC to CategorySubclassVC:
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "categoryID") as! CategorySubclassVC
self.navigationController?.pushViewController(secondViewController, animated: true)
I get an error: Could not cast value of type 'My_App.CategoryVC' to 'My_App.CategorySubclassVC'.
Is it possible to push view controller in this way? If I change the destination to CategoryVC all works fine. The problem appears for Subclass (CategorySubclassVC).
I found a solution that works for me.
let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "categoryID") as! CategoryVC
object_setClass(secondViewController, CategorySubclassVC)
self.navigationController?.pushViewController(secondViewController, animated: true)

How do I pop out a ViewController(like a modal) programmatically?

All:
I am doing a swift , cocoa macos app project with multiple ViewControllers in one storyboard.
I know if I do segue and link the segue from second ViewController to first ViewController.
I can pop out ViewController.
But what if I have a function and want to call another ViewController to present programmatically from first ViewController?
I search a lot of examples start with UIStoryBoard, but my Storyboard is NSStoryboard.
Could anyone hint me a little to start with?
my code:
func checkPassword(SystemMsg:String) -> String{
//print("x")
let storyBoard = NSStoryboard(name: "Main", bundle: nil)
let mainViewController : NSViewController = storyBoard.instantiateController(withIdentifier: "PasswordInput") as! NSViewController
//self.present(mainViewController, asPopoverRelativeTo: <#T##NSRect#>, of: sender, preferredEdge: NSRectEdge, behavior: <#T##NSPopover.Behavior#>)
return ""
}
And my viewController in storyboard look like(no segue,no link):
enter image description here
If anyone can guide me through this step by step would be appreciated.
I figure it out myself.
The most simple way contains 4 steps:
Identify your main ViewController and second ViewController in storyboard
Create your main ViewController with instantiateController
Create your second ViewController with instantiateController
use presentAsModalWindow or presentAsSheet to present secondController on main ViewController
first we need to Identify storyboard correctly:
Identify first storyboard , we need to click top blue cube and then name it in Storyboard Id area
Identify second storyboard , we need to click top blue cube and then name it in Storyboard Id area
example code:
func checkPassword(SystemMsg:String) -> String{
//print("x")
//let storyBoard = NSStoryboard(name: "Main", bundle: nil)
let mainViewController : NSViewController = self.storyboard?.instantiateController(withIdentifier: "MainController") as! NSViewController
let passwordInputViewController : NSViewController = self.storyboard?.instantiateController(withIdentifier: "PasswordInput") as! NSViewController
mainViewController.presentAsModalWindow(passwordInputViewController)
//Or
mainViewController.presentAsSheet(passwordInputViewController)
return ""
}
If I miss something please correct me gently.
reference: Transitioning between view controller, OS X
PS. if you want to pass value between ViewController, this is good reference : https://learnappmaking.com/pass-data-between-view-controllers-swift-how-to/#back-properties

instantiateViewController throws SIGABRT on Unit Test

I'm writing unit tests for a controller that has elements (like buttons) created in storyboard. I'm trying to instantiate the view controller in my Unit Test so I can access those elements and not have the app crash. However, the way that I am trying to instantiate my storyboard is causing Xcode to throw a SIGABRT error on the line that I am calling the instantiateViewController command.
This is the code that I am using to try and instantiate my storyboard in my unit test file:
func testAreaActionsViewController() {
let storyboard = UIStoryboard(name: "Main", bundle: Bundle(for: self.classForCoder))
let viewController = storyboard.instantiateViewController(withIdentifier: "AreaActionsViewController") as! AreaActionsViewController
// view.loadView()
//
// view.viewDidLoad()
}
Does anyone have an idea as to why it is throwing SIGABRT? I verified in my storyboard that the Identifier is AreaActionsViewController, and also set the target in my Main.storyboard to include my testing target.
Sorry for late but I had same issue and I solved removing this:
#testable import ProjectName

How to call function from viewcontroller in appdelegate? (Swift)

So basically I am trying to run the function Refresh (Located in ViewController.swift) in AppDelegate.swift. After searching for the better part of 5 hours I can't figure out why this isnt working.
here is my function:
func Refresh(){
if(Count==0 && QuickActionChecker==true){
Previous.text=String(TipArray[CountCurrent])
Current.text=String(TipArray[CountCurrent])
Deliveries.text="\(Runs)"
}
if(Count>0 && QuickActionChecker==true){
Previous.text=String(TipArray[CountCurrent-1])
Current.text=String(Total)
Deliveries.text="\(Runs)"
}
}
In my Appdelegate.swift I have initialized the ViewController by setting it to Main:
let Main = ViewController()
and here is where I'm attempting to run the function from (force touch quick action from the homescreen):
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: #escaping (Bool) -> Void) {
if shortcutItem.type == "com.Sicarix.Ticket.Add5"{
if(CountCurrent==0){
TipArray.append(5)
Count=Count+1
CountCurrent=CountCurrent+2
Total=TipArray.reduce(0) {$0+$1}
Runs=Runs+1
QuickActionChecker=true
Main.Refresh()
}
else{
TipArray.append(5)
Count=Count+1
CountCurrent=CountCurrent+1
Total=TipArray.reduce(0) {$0+$1}
Runs=Runs+1
QuickActionChecker=true
Main.Refresh()
}
}
}
however, when I try to use the shortcut it opens the app to a white screen and stays there. Console is spitting out:
fatal error: unexpectedly found nil while unwrapping an Optional value
the code runs fine when I remove the Main.Refresh(), it just doesn't update the labels in my app, hence the need for the function.
Please help, I'm so ready to move on past this bug....
also please bear in mind that I haven't even been coding in swift for a week yet, so please break down what was wrong as best you can. TIA
Change your viewController object
let nextVC = storyboard?.instantiateViewController(withIdentifier:"ViewController") as! ViewController
The problem is that you are instantiating a new ViewController using ViewController() and that ViewController isn't added to your view controller hierarchy.
You need to use your Storyboard to instantiate the ViewController by using let mainVC = UIStoryboard(name: "Main", bundle: "nil").instantiateViewController(withIdentifier:"ViewController") as! ViewController and make sure your identifier is set up in Storyboard.
If you are not using Storyboard, another solution is to store the text you want to display on the UI in data source variables and only update those variables in 'AppDelegate', then load the content of those variables onto your labels in 'viewDidLoad' or 'viewWillAppear'.