Do I use storyboards right? - swift

Am I using storyboards right?
I have several storyboards with view controllers in them.
When I need to navigate between them, I do the following:
I do create a new instance of the storyboard let storyboard = ... every time I use them. Is this a bad method? Or Should I declare it in the view did load?
func detailsRequestedForMessage(message: message) {
let storyboard = UIStoryboard(name: "Additional", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "MessageDetailsViewController") as! MessageDetailsViewController
viewController.messageId = message.id
self.navigationController?.pushViewController(viewController, animated: true)
}
func viewAllMessage() {
let storyboard = UIStoryboard(name: "Additional", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "AllMessageViewController") as! AllMessageViewController
self.navigationController?.pushViewController(viewController, animated: true)
}
func viewOptions() {
let storyboard = UIStoryboard(name: "Options", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier: "OptionsViewController") as! OptionsViewController
self.present(viewController, animated: true)
}

It costs nothing to store a storyboard reference, but takes time to resolve a storyboard reference from a name. So I would say that you should put lines like
self.storyboardAdditional = UIStoryboard(name: "Additional", bundle: nil)
in viewDidLoad, storing the references in properties, and thereafter refer to the storyboards by way of the properties.

Related

Swift: Can't use navigationController.pushViewController inside a function

I am transitioning between views programatically using the code below and it gets repeated quite a lot so I wanted to create a global function but I can't seem to be able to get the hang of it.
The code works when called inside a ViewController class, so I suppose the problem is that my function doesn't know which VC do I want to call navigationController.pushViewController on, but I don't know how to reference the VC either as an argument passed to the function, or better yet with something like .self to take the current VC class the function is called in.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ExamplesControllerVC")
self.navigationController?.pushViewController(vc, animated: true)
The error I get if I try to run that as a function in a separate file is:
Use of unresolved identifier 'navigationController'; did you mean 'UINavigationController'?
So the function I'd like to create and call is something like:
showVC("ExamplesControllerVC")
Any ideas?
Whatever function this code is in needs to be updated to take a parameter of type UIViewController.
func showMain(on vc: UIViewController) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ExamplesControllerVC")
vc.navigationController?.pushViewController(vc, animated: true)
}
Now you can call this as:
showMain(on: someViewController)
Or add this function to an extension on UIViewController then your use of self works just fine.
extension UIViewController {
func showMain() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "ExamplesControllerVC")
self.navigationController?.pushViewController(vc, animated: true)
}
}
Do you want to do something like this?:
extension UIViewController {
func presentView(withIdentifier: String) {
if let newVC = self.storyboard?.instantiateViewController(withIdentifier: withIdentifier) {
self.present(newVC, animated: true, completion: nil)
}
}
}
You can call it this way:
self.presentView(withIdentifier: "yourIdentifier")

Can't instantiate view controller Swift

My code worked well so far. I do not see what I could change to make it bug like that. When I want to return the user to the home page, it works, but a few seconds after the previous ViewController reappears on the screen.
I've tried to change "as! HomeViewController" with "as UIViewController" or as NavigationViewController but it keeps going to the previous ViewController.
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let balanceViewController = storyBoard.instantiateViewController(withIdentifier: "home") as! HomeViewController
self.present(balanceViewController, animated: true, completion: nil)
When I want to return the user to the home page...
You should use UINavigationController.popToViewController(_:animated:) to return to a UIViewController:
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let balanceViewController = storyBoard.instantiateViewController(withIdentifier: "home") as! HomeViewController
self.navigationController?.popToViewController(balanceViewController, animated: true)

Is it possible to make the compiler see VC name (from string) as that VC type?

I'm trying the make a function that receives a name (String) of a View Controller and then presents that View Controller modally. Example:
func presentViewControllerModally(newViewControllerName: String){
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let newViewController = storyBoard.instantiateViewController(withIdentifier: "\(newViewControllerName)_Identifier") as! newViewControllerName
self.present(newViewController, animated: false, completion: nil)
}
But I can't make the as! newViewControllerName to work as if the newViewControllerName is the type of the newViewControllerName and not the object itself.
You don’t have to downcast controller to your custom subclass. You can just present UIViewController.
If you have to get the real viewController type in return, just use generics to do this.
func presentViewControllerModally<T: UIViewController>(newViewControllerName: String) -> T? {
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let newViewController = storyBoard.instantiateViewController(withIdentifier: "\(newViewControllerName)_Identifier") as? T {
self.present(newViewController, animated: false, completion: nil)
return newViewController
}
return nil
}
Then call
let myVC: MyVController = presentViewControllerModally(newViewControllerName: "MyVC")
If you don't need the right type but just UIViewController, you don't need a cast.

Programmatically Segue Between Views in Different Storyboard Files

I have a view HostViewController in Host.storyboard and, in storyboard I am able to segue to AttendDetailViewController in Main.storyboard. However, I want to do this programmatically as follows:
private func attendDetailViewControllerSegue(event: CAEvent) {
let vc = AttendDetailViewController(nibName: "AttendDetailViewController", bundle: nil)
vc.event = event
navigationController?.pushViewController(vc, animated: true)
}
However, when I run this I get the following error:
'NSInternalInconsistencyException', reason: 'Could not load NIB in bundle: 'NSBundle ... with name 'AttendDetailViewController''
I have tried every solution on the internet for this problem, and I feel it might have something to do with the Views being in different storyboards.
Any thoughts on how to do this segue without an exception?
You have to get the storyboard from the bundle:
var hostStoryboard = UIStoryboard(name: "Host", bundle: Bundle.main)
Then, instantiate and present the view controller:
let someViewController = hostStoryboard?.instantiateViewController(withIdentifier: "AttendDetailViewController") as? AttendDetailViewController
self.navigationController?.present(attendDetailViewController!, animated: true)
Don't forget to set the AttendDetailViewController's storyboard identifier in the storyboard.
I also recommend you safely unwrap attendDetailViewController before using it:
let someViewController = hostStoryboard?.instantiateViewController(withIdentifier: "AttendDetailViewController") as? AttendDetailViewController
if let vc = attendDetailViewController {
self.navigationController?.present(vc, animated: true)
}
1- to load from xib ( used when there is AttendDetailViewController.xib file in your project )
let vc = AttendDetailViewController(nibName: "AttendDetailViewController", bundle: nil)
2- to load from storyboard ( used when the vc is inside a storyboard )
let vc = UIStoryboard(name: "Host", bundle: nil)!.instantiateViewController(withIdentifier: "vciD") as? AttendDetailViewController
3- load programmatically ( used when the vc layout is created programmatically )
let vc = AttendDetailViewController()

How to pass data to ViewController in new storyboard

I use this code for go to new story board and pass variable X to a viewController in new storyboard
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let homeViewController: UIViewController = mainStoryboard.instantiateViewController(withIdentifier: "ViewControllerCall")
homeViewController.myVar = "x"
self.present(homeViewController, animated: true, completion: nil)
But I get this error:
Value of type 'UIViewController' has no member 'myVar'
In this line:
let homeViewController: UIViewController = mainStoryboard.instantiateViewController(withIdentifier: "ViewControllerCall")
You are saying "Instantiate a view controller from the storyboard, and infer its type to be a UIViewController". However, what you need to do is specifying it as ViewControllerCall.
You can do this by type casting - adding as? ViewControllerCall. (Or as! ViewControllerCall, if you don't want to use the if let clause).
Your code should look like this:
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let homeViewController = mainStoryboard.instantiateViewController(withIdentifier: "ViewControllerCall") as? ViewControllerCall {
homeViewController.myVar = "x"
self.present(homeViewController, animated: true)
}
Or
let mainStoryboard = UIStoryboard(name: "Main", bundle: nil)
let homeViewController = mainStoryboard.instantiateViewController(withIdentifier: "ViewControllerCall") as! ViewControllerCall
homeViewController.myVar = "x"
self.present(homeViewController, animated: true)
Note that by using as! in the second way I've presented, it will crash your app if the view controller with identifier ViewControllerCall is not of type ViewControllerCall.
You have to change second line code only.
1. Go to your Main storyboard and check the class for ViewController for which you want to pass variable and set storyboard identifier same as class name.
2. Now if your class name is "ViewControllerCall" then change your line code as below:
let homeViewController = mainStoryboard.instantiateViewController(withIdentifier: "ViewControllerCall") as! ViewControllerCall