Swift Multiple Storyboard - How to access specific one - ios

I had split a couple profiles into multiple storyboards to try to help with my freeze time because I heard to many storyboards can be the cause of why Xcode would just "freeze" for up to 10 mins sometimes causing a crash in my flow of work. So, I need to access each specific storyboard, yet don't know how. This current code accesses the "Main.Storyboard" file yet, I need to call other storyboard files. Here I am checking if a specific profile is signed in and pushing them to the profile. I had all the profiles in one storyboard but now I separated them. Just need to know how to push to other storyboards. Thanks
func checkIfBusinessLoggedIn() {
Auth.auth().addStateDidChangeListener({ (auth, user) in
if (user != nil) {
Database.database().reference().child("Businesses").child((user?.uid)!).observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists() {
print("Business is Signed In")
let vc = self.storyboard?.instantiateViewController(withIdentifier: "Business Profile")
self.present(vc!, animated: true, completion: nil)
}
})
}
})
}

let storyboard = UIStoryboard(name: "your_storyboard_name", bundle: nil)
vc = storyboard.instantiateViewController(withIdentifier: "your_view_controller_name")
//to push that controller on the stack
self.navigationController?.pushViewController(vc, animated: true)
If your storyboard file is "Business.storyboard", "your_storyboard_name" name should be "Business".

If you want to access other storyboards you need to give that name.
self.storyboard will always point to your default storyboard. Instead do this :
let VC = UIStoryboard(name: "VCStoryBoardName", bundle: nil).instantiateViewController(withIdentifier: "InstructionScreenController") as! VC

Use below code for multipe storybord and create ENUM for storyboard name that is easy to specify storyboard name.
//MARK:- Enum_StoryBoard
enum enumStoryBoard:String {
case main = "Main"
case home = "HomeSB"
}
let storyBoard = UIStoryboard.init(name: enumStoryBoard. home.rawValue, bundle: nil)
let objLocationSearch = storyBoard.instantiateViewController(withIdentifier: "LocationSearch") as? LocationSearch
self.navigationController?.pushViewController(objLocationSearch!, animated: true)

Using enum and generic method to return the required object.
enum AppStoryboard : String {
case First, Second
var instance : UIStoryboard {
return UIStoryboard(name: self.rawValue, bundle: Bundle.main)
}
func instantiateVC<T : UIViewController>(viewControllerClass : T.Type) throws -> T {
let storyboardID = (viewControllerClass as UIViewController.Type).storyboardID
guard let viewObj = instance.instantiateViewController(withIdentifier: storyboardID) as? T else {
throw ExcpectedError.intantiationErro(msg:"ViewController with identifier \(storyboardID)")
}
return viewObj
}
}
use this code to instantiate your viewController
let second = try! AppStoryboard.Second.viewController(viewControllerClass: SecondViewController.self) self.present(second, animated: true, completion: nil)

Related

Swift problem with sending data to another ViewController

Variable cards in second ViewController should be updated in first VC on buttonClick.
I tested the sending data from the first VC with printing data and it works good.
Here is the code:
#IBAction func btnTapped(_ sender: Any) {
let mainStoryboard = UIStoryboard(name: "Main", bundle: Bundle.main)
let secondVC = (mainStoryboard.instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController)
for card in setsOfCardsByLevel {
cardVC.cards.append(card)
}
print("Number of cards to send - \(cardVC.secondVC.count)") //PRINTS RIGHT NUMBER
presentVC("SecondViewController") //WORKS FINE
}
Maybe you dont need this method below but I will put it just in case:
func presentVC(_ VC_Name: String) {
guard let myVC = self.storyboard?.instantiateViewController(withIdentifier: VC_Name) else { return }
let navController = UINavigationController(rootViewController: myVC)
navController.modalPresentationStyle = .fullScreen
self.navigationController?.present(navController, animated: true, completion: nil)
}
PROBLEM: When I print cards in the secondViewController, they are empty(count = 0). How?
EDIT: While waiting for answer on StackOverflow I changed presentViewController to pushViewController and print in secondVC shows right number. I am confused.
Your problem is in your presentVC method. First in your btnTapped method you instantiate your second viewController and assing your cards, again in your presentVC method instantiate another controller and it's not relevant to the first you define in your btnTapped and cards variable not assigned. For solution you can pass your secondVC as a parameter to your presentVC, not the name of storyboard

"Attempt to present while already presenting" still appearing after checks?

let appDelegate = UIKit.UIApplication.shared.delegate!
if let tabBarController = appDelegate.window??.rootViewController as? UITabBarController {
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let signInVC = storyboard.instantiateViewController(withIdentifier: "SignInVC") as! SignInVC
guard !signInVC.isBeingPresented else {
log.warning("Attempt to present sign in sheet when it is already showing")
return
}
signInVC.modalPresentationStyle = UIModalPresentationStyle.formSheet
tabBarController.present(signInVC, animated: true, completion: nil)
}
This code can be called multiple times despite signInVC being presented. I already added this check:
guard !signInVC.isBeingPresented else {
log.warning("Attempt to present sign in sheet when it is already showing")
return
}
but it doesn't seem to prevent this error:
Warning: Attempt to present <App.SignInVC: 0x101f2f280> on <UITabBarController: 0x101e05880> which is already presenting <App.SignInVC: 0x101f4e4c0>
Your guard isn't a valid check. The isBeingPresented is being called on a brand new view controller instance that hasn't yet been presented. So isBeingPresented will always be false. Besides that, that property can only be used from within a view controller's view[Will|Did]Appear method.
What you want to check is to see if the tabBarController has already presented another view controller or not.
And lastly, only create and setup the sign-in view controller if it should be presented.
let appDelegate = UIKit.UIApplication.shared.delegate!
if let tabBarController = appDelegate.window?.rootViewController as? UITabBarController {
if tabBarController.presentedViewController == nil {
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let signInVC = storyboard.instantiateViewController(withIdentifier: "SignInVC") as! SignInVC
signInVC.modalPresentationStyle = UIModalPresentationStyle.formSheet
tabBarController.present(signInVC, animated: true, completion: nil)
}
}

How to reopen application after login

I'm an android developer. in android, when user login in application, I will re-open the MainActivity class ( controller ) to refresh some views.
in iOS applications, how to do this scenario ?
You can reopen you default/LandingViewController.
Suppose you have a View Controller with name LandingViewController
When you successfully logged in all you need is to re instantiate the LandingViewController
In AppDelegate class make a function with name
func userDidLoggedIn(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)//Replace Main With your own storyboard name containing LandingViewController
let landingViewController = storyboard.instantiateViewController(withIdentifier: "LandingViewControllerIdentifier")//Pass your LandingViewController Identier that you have set in your storyboard file.
guard let subviews = self.window?.subviews else {
return
}
for view in subviews {
view.removeFromSuperview()
}
self.window?.rootViewController = landingViewController
}
Now Simply Call this Function where ever in the entire project like this In your case write below lines in the completion block of login request API.
let delegate = UIApplication.shared.delegate as! AppDelegate
delegate. userDidLoggedIn()
Once user login, you can change your rootviewcontroller like this:
var nav_VC: UIViewController?
func onSuccessfulLogin()
{
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
nav_VC = nil
if nav_VC == nil {
nav_VC = storyboard.instantiateViewController(withIdentifier: "home_nav")
}
self.window?.rootViewController = nav_VC
self.window?.makeKeyAndVisible()
}

opening a tab bar controller on successful login swift

I am extremely new to swift and storyboards. I have an initial view set which presents the user with login or register options. on the success of my login web service, I am trying to open a tab bar. I am getting into the success of the webservice as I can see the response.
My code for attempting to load the tab bar is as folllows in my initial view controller:
func loadHomeScreen()
{
emailField.text = ""
passwordField.text = ""
self.presentViewController(UIStoryboard.tabbarController()!, animated: true, completion: nil)
}
And at the very bottom of that file, I have the following:
private extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) }
class func tabbarController() -> TabbarController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("TabbarControllerID") as? TabbarController
}
}
And in my storyboard I have given the tabbarcontroller the id above. When I run the app (tested on the simulator for iphone6), I am getting the error 'found nil while unwrapping an Optional value' and this is pointing to the line of code in my loadHome func above (self.presentViewController(UIStoryboard.tabbarController()!, animated: true, completion: nil))
Any help would be appreciated
You could instead instantiate your storyboard like so and the code would look like this under loadHomeScreen():
emailField.text = ""
passwordField.text = ""
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("TabbarControllerID") as! TabbarController
self.presentViewController(vc, animated: true, completion: nil)
You may need to change the bundle to the "mainBundle" as you have in your code, but this should work.
This may not be the optimal solution if your plan is to extend UIStoryboard for instantiating your ViewControllers but I think this might be a little easier/cleaner, depending on how your project is set up.

XCode 6: I can't select a view by its storyboard ID

This is the code :
let theBoard = self.storyboard!
let vc = theBoard.instantiateViewControllerWithIdentifier("myViewId") as! UIViewController // this line causes the error
The second line causes this error:
unexpectedly found nil while unwrapping an Optional value
* VERY IMPORTANT * : the error only fires when the build configuration is set to "debug". It works well when set to "release".
Why ?
You are casting UIViewController in the let vc = theBoard.instantiateViewControllerWithIdentifier("myViewId") as! UIViewController which is wrong.
You should try:
var storyboard = UIStoryboard(name: "Main", bundle: nil)
var vc = storyboard.instantiateViewControllerWithIdentifier("myViewId") as! ViewController
It all is a matter of the new syntax, functionality hasn't changed:
let storyboard = UIStoryboard(name: "MyStoryboardName", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("someViewController") as! UIViewController
self.presentViewController(vc, animated: true, completion: nil)
Use like this
var moveToNextVC:ViewController = self.storyboard?.instantiateViewControllerWithIdentifier("myViewId") as! ViewController
self.presentViewController(moveToNextVC, animated: true, completion: nil)
Remember set StoryboardID Identify 'myViewId' in File Inspector for your viewcontroller

Resources