how to set class to viewcontroller programmatically in swift? - 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

Related

Is it okay to define a custom view using Storyboard / NSViewController instead of XIB file

I always find it a little bit annoying (for lack of a better word) to define a custom NSView in a XIB file.
I have switched to using a custom Storyboard (one per class) and a corresponding NSViewController. When adding the view to my hierarchy I am simply using code that looks like this:
let viewController: CustomViewController = // Use my custom extensions to instantiate the NSViewController subclass.
self.view.addSubview(viewController.view)
Are there any drawbacks? Is it okay to use this approach or could there be any downsides (maybe a NSViewController instance receives additional events or is somehow added to another hierarchy by default).
As a general rule, a view controller in a Storyboard is backed by code in its controller class.
.instantiateViewController(...) is then used to load a view controller from a Storyboard and:
push it
present it
add it as a child view controller, most often followed by adding its view to the current hierarchy
If you do have code backing that view controller, and you haven't added it as a child or assigned it to a class var/property, then the controller code will no longer exist as soon as viewController goes out of scope. If you've connected any UI elements to that controller class, or you need to call any funcs in that class, you'll run into problems.
However, assuming you do not have any code associated with that VC, then it's really functionally equivalent to designing the view in a XIB and instantiating it with from the resulting Nib.

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

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.

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.

Segue to UIViewController in different UINavigationController

I have the following setup:
I have a UIViewController embedded in a UINavigationController. I have a second UIViewController embedded in a second UINavigationController. I need to call the second UIViewController in two ways. I have two buttons set up on the first UIViewController. The first button has a segue created by control-click-drag between the button and the second UINavigationController. When I click the button, the second view controller displays. The second button calls the created segue using performSegueWithIdentifier like this:
#IBAction func segueTwoButton(sender: AnyObject) {
performSegueWithIdentifier("ShowSecondView", sender: self)
}
In both cases the second view controller displays correctly.
But, I have some information that I want to pass. So in the prepareForSegue function, I have this:
let vc = segue.destinationViewController as! SecondViewController
This fails, because the segue is going through the second UINavigationController to get to the second UIViewController.
I've come up with two ways of handling this.
I can use the following in the prepareForSegue
let vc = segue.destinationViewController.childViewControllers[0] as! SecondViewController
I can add a UINavigationController class and then pipe it through there, but that seems like a lot of work to do this.
Both of these ways work, but is this the best way, or is there some other way that I should consider handling it?
Second question - Assuming that I do it this way and put a UINavigationController class in so that I can pass the information along, what is the proper way to unwind? I can create an unwind segue that will go straight from the second view controller, back to the first, but is it acceptable to do it this way? Or would I need to unwind to the UINavigationController class and then to the first view controller?
I'm not sure why you decided to embed another navigation controller within another. Generally, if you embed a navigation controller one time that will suffice for the rest after you've set up your other segues. So first thing's first delete that second nav controller.
Secondly, from experience, I would always avoid control dragging from anything other the view controller itself. Otherwise, you get duplicate calls to perform segues. So go ahead and delete all remnants of the prior segues you created with the buttons. Instead, Control drag from the top yellow view controller icon to the next view controller. make sure you name the segue arrow so that you can properly performsegueWithIdentifier
Now you should have First View directly segued to the Second View. This will be easier to pipe or funnel information through the segue function exactly like you had coded above
let vc = segue.destinationViewController as! SecondViewController
Global Variable ( will replace segue funneling information )
Lets take this as an example. Say I wanted to recored the user's name on the first view controller.
// First View Controller
private let userName: String
// After that we can create a new object called NameSingleton
class NameSingleton {
// NameSingleton.swift
var UserName:String = " " // Since its global this value will change
static let sharedinstance = NameSingleton()
}
// Now hop back over to your first ViewController
// before you trigger your segue assign whatever data you need to your
// singleton. In this case, a name.
// First View Controller.swift
#IBAction func segueTwoButton(sender: AnyObject) {
NameSingleton.sharedInstance.UserName = "Hi"
performSegueWithIdentifier("ShowSecondView", sender: self)
}
// Now if you go to the second view controller and access it, the name
// will be stored in the singleton. (Great for moving huge pieces info
// (i.e. structs, objects, lists)

ViewController.Type does not have a member named view

I have created a new project in Swift, and added a second ViewController to the storyboard, added a class file and linked it. Now I'm trying to access the view of the new vc in the initial vc:
let settingsView = SettingsViewController.view
But it returns the error:
SettingsViewController.Type does not have a member named view
Why is that? How can the view controller not have a view? The view is connected correctly to the VC in the Connections Inspector.
This is, because you try to access view on the type itself, but not on the instance. That means you have to instantiate the view controller first and after that you can access its members, i.e. (pseudo-swift-code)
let settingsVc = SettingsViewController()
addNewVcToHierarchy(settingsVc)
let settingsView = settingsVc.view
However, usually you do not want to access the view directly from a view controller where it does not belongs to but instead push data into the view controller (e.g. on a segue).