Loading a instance of a class dynamically in swift - ios

I would like to init an instance of a class specified at runtime. I have the following set up.
//MyClass1 & MyClass2 inherit from UIViewController
let arr = MyClass1.self,MyClass2.self]
func didSelectRow(index:Int) {
var controllerRef = arr[index] //This returns a .Type
var controllerClass = controllerRef() //This returns “the class” MyClass1
//This is where I'm stuck. How do I initialize controller??
//Finally, push VC
self.navigationController?.pushViewController(controller, animated: true)
}
To initialize the VC with Objective C, I was able to do this as follows:
UIViewController *controller = [((UIViewController *)[controllerClass alloc]) initWithNibName:class1Nib bundle:nil];
With Swift, I’m expecting something like the syntax below, but it doesn’t work
//Doesn’t work
var controller = (controllerClass as UIViewController)(nibName: class1Nib, bundle:nil)
//Overriding the init method in MyClass1 and then calling the below doesn’t work
var controller = controllerClass(nibName: class1Nib, bundle:nil)
//The below works, but viewDidLoad() does not get called. It only loads the nib file
var controller = UIViewController(nibName: Class1Nib, bundle:nil)
Any help is appreciated. Thanks

Your question, with its repeated and meaningless "doesn't work" and "the below", is confused and confusing, so I will respond by assuming that what you want to do is instantiate and push a view controller given its class, and I will simply demonstrate in a few lines of code that this is possible.
Here is a function that returns a UIViewController subclass:
func provideVCClass() -> UIViewController.Type {
return ViewController.self
}
And here are lines from a button's action handler where we call that function and use the class to instantiate that view controller and push it onto the current navigation controller:
let vcclass = provideVCClass()
let vc = vcclass(nibName: "View", bundle: nil)
self.navigationController?.pushViewController(vc, animated: true)
I assure you that this does work: the correct view controller subclass is instantiated, its nib is loaded, and its view does appear in the interface.
So all I can say in answer to your question, which I don't fully understand, is Go Ye And Do Likewise.
EDIT Here's a little postscript. If your code actually says:
let arr = [MyClass1.self, MyClass2.self]
I would suggest you try saying
let arr : [UIViewController] = [MyClass1.self, MyClass2.self]
...just in case Swift is not inferring the correct type here.

Related

Instance Member Cannot Be Used on Type when pushing UIViewController

I am programmatically setting up ViewControllers (no storyboard).
I want to pass data to the next VC, and while I know how to do that with a segue and a storyboard, I can't figure out how to do it purely programmatically.
I get the error "Instance Member Cannot Be Used on Type..."
// Create Next View Controller Variable
let nextViewController = CarbonCalculatorResultsViewController()
// Pass data to next view controller. There is already a variable in that file: var userInformation: UserInformation?
CarbonCalculatorResultsViewController.userInformation = userInformation
// Push next View Controller
self.navigationController?.pushViewController(nextViewController, animated: true)
Do I need to instantiate the next VC before I can pass data? That's what this answer seems to talk about yet I don't have a Storyboard. Thanks!
Step 1: Setup your destination class
In CarbonCalculatorResultsViewController class, declare a var to receive data like so:
class CarbonCalculatorResultsViewController: UIViewController {
var foo: String? {
didSet {
// What you'd like to do with the data received
print(foo ?? "")
}
}
ovevride func viewDidLoad() {
//
}
}
Step 2: Prepare data in your source class
let nextViewController = CarbonCalculatorResultsViewController()
// You have access of the variable in CarbonCalculatorResultsViewController
nextViewController.foo = <data_you_want_to_pass>
// Push next View Controller
self.navigationController?.pushViewController(nextViewController, animated: true)
Then, every time the CarbonCalculatorResultsViewController comes alive, the didSet{} of foo would be called.
The current sample code (above) is setting a value to a static variable (owned by the CarbonCalculatorResultsViewController.Type.
What I believe you want to implement is the following instead:
// Create Next View Controller Variable
let nextViewController = CarbonCalculatorResultsViewController()
// Pass data to next view controller. There is already a variable in that file: var userInformation: UserInformation?
nextViewController.userInformation = userInformation
// Push next View Controller
self.navigationController?.pushViewController(nextViewController, animated: true)
This sample code is setting a value to the instance variable userInformation on the type nextViewController.
You should pass the variable on the Object, not on the Class
Replace: CarbonCalculatorResultsViewController.userInformation = userInformation
With:
nextViewController.userInformation = userInformation
Note:
CarbonCalculatorResultsViewController is the Class.
nextViewController is the Object.
Your full code should look like this:
// Create Next View Controller Variable
let nextViewController = CarbonCalculatorResultsViewController()
// Pass data to next view controller. There is already a variable in that file: var userInformation: UserInformation?
nextViewController.userInformation = userInformation
// Push next View Controller
self.navigationController?.pushViewController(nextViewController, animated: true)

unwrap optional value nil

I am new in ios developer.I pass my data to one view controller to another using function
var vc = MessageViewController()
vc.printSomething(address: address,id: id)
I pass address and id to MessageViewController.In MessageViewController
func printSomething(address: String,id: String) {
print("address",address)
print("id",id)
address_text.text = "\(address)"
}
Above code I print address,id but when i set address on UITextField address.text I got unwrapping error.
I used to create a class method to instantiate view controller from storyboard like this...
class MessageViewController: UIViewController {
//other methods
//MARK:- Class Method
class func viewController() -> MessageViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "<Storyboard Id>") as! MessageViewController
}
}
You need to just use to instantiate view controller
var vc = MessageViewController.viewController()
You can set storyboard identifier like in below image.
Hi it is the issue with variable not assigned to address_text beacuse xib not initialized at that time of your method(printSomething) calling, before that take two varibles for saving the address and id in MessageViewController and assign id and address to those variables in printSomething method and you can assing those values to address_text in viewdidload method of MessageViewController

Could not cast value of type 'UITabBarController' to 'ViewController'

The situation: when I press a button in rentViewController, it pops up a tableviewcontroller. If a specific cell has been pressed, it sends data to rentViewController. In order to send data from one view controller to another I needed the code
let rentViewController : RentViewController = self.presentingViewController as! RentViewController <- here is where the error shows up
so that tableviewcontroller could get access to the variables and functions from rentviewcontroller. I'm using
self.dismiss(animated: true, completion: nil)
to get out of the tableviewcontroller and back to the rentviewcontroller. However, it gives me an error "Could not cast value of type 'UITabBarController' to 'RentViewController'". I did some research and I think it's according to the orders of my view controllers but I'm not sure how to change it in a way that it works. My initial view is 'TabBarController' and the order after that is 'NavigationController' -> 'RentViewController' -> 'TableViewController'. If you have questions feel free to ask I can provide you more information.
Your viewController is being presented from UITabBarController. With approach you are using I believe you can access it like this (where index is index in UITabBarController of your UINavigationController containing RentVC):
if let tab = self.presentingViewController as? UITabBarController,
let nav = tab.viewControllers?[index] as? UINavigationController,
let rentViewController = nav.viewControllers.first as? RentViewController {
rentViewController.data = data
}
However, I would suggest using a delegate or callback block to pass data in this occassion.
For delegate approach, first create protocol:
protocol PassDataDelegate:class {
func passData(data:YourType)
}
Then in TableViewController:
class TableViewController: UIViewController {
weak var delegate: PassDataDelegate?
}
And in RentViewController:
extension RentViewController: PassDataDelegate {
func passData(data:YourType) {
//use data to suit your needs
}
}
Before presenting TableViewController, in RentViewController, set its delegate:
tableViewController.delegate
present(tableViewController, animated: true)
And finally, inside TableViewController, before dismissing, call delegate's method to pass data:
delegate?.passData(data: <<someData>>)

How can I call a function of UIViewController from its reference on a different swift file?

I have got my current visibleViewController on a swift file:
let currentController = self.navigationController?.visibleViewController
and I have a function on the swift file associated to the actual UIViewController called chargeData.
What I am trying to do is to call that function from the first file but I am not able to do it. I tried doing:
currentController.chargeData()
but an error is shown:
Value of type 'UIViewController?' has no member 'chargeData'
I can guess from the error log that of course it is not recognizing the swift file in which the actual UIViewController is associated.
So what should I do to access to the function of my actual UIViewController from its reference?
Thanks in advance!
You have to typecast your currentController variable in order to call any method you created on your own.
let currentController = self.navigationController?.visibleViewController as? YourViewController //Here YourViewController is the name of the UIViewController subclass you created
Hope this helps.
You're not far away: you have access to the view controller, but Swift doesn't realize what type it is – it thinks it's a plain UIViewController? when really you know it isn't.
What you need is a typecast. If your class was called ViewController, you would write this:
if let currentController = self.navigationController?.visibleViewController as? ViewController {
currentController.chargeData()
}
If you don't want to cast your currentController from UIViewController to your specific type, then you can use Objective-C's performSelector:
let selector = Selector("chargeData")
if currentController?.respondsToSelector(selector) == true {
currentController!.performSelector(selector)
}
let currentController = self.navigationController?.visibleViewController
NavigationController's VisibleViewController method return UIViewController Type so your method is not find in UIViewController.
let currentController = self.navigationController?.visibleViewController as! YourVC.
Now you can call your method.
In Swift Type is not implicitly find.

NSClassFromString in Swift 2.1

I guess I read most of the SO questions and Google answers on this, but still cannot get it working. Take this code example:
import UIKit
class Person: NSObject {
var name = "Pieter"
}
let PersonClass: AnyClass? = NSClassFromString("Person")
let person = PersonClass()
print(person.name)
Changing NSClassFromString("Person") into NSClassFromString("MyAppName.Person") does not make a difference. Adding as! NSObject.Type behind the PersonClass declaration neither. Adding #obcj(Person before the class declaration neither.
The real-life goal is to read class names from a database and load the appropriate storyboard / UIViewController accordingly. So I am looking for a way to pass the UIViewController class name as a String and instantiate the UIStoryboard with it.
How to do that?
Edit: here is what I currently try for the real-life scenario after your suggestions:
let storyboard = UIStoryboard(name: "MyStoryboard", bundle: nil)
let MyClassName = "MyApp.MyTableViewController"
let MyClass = NSClassFromString(MyClassName) as! UITableViewController.Type
let controller = storyboard.instantiateInitialViewController() as! MyClass
It gives an error saying "MyClass" is not a type. I cannot remove it or I will get an error that it cannot convert from AnyClass.
Try the code given below
let viewController = storyboard?.instantiateViewControllerWithIdentifier("ViewController") as! ViewController
This code allow you to instantiate the storyboard view controller.
But you can not pass the View Controller class name.
You have to pass the storyboard identifier of the view controller.
Please review the below picture for it.

Resources