ios swift - instantiating same VC from multiple tabs - ios

I have multiple tabs connected via separate navigation controllers. I want to to instantiate same End Pt VC, irrespective of which tab user chooses tabs 1 through 4.
When I select Tab 1 it shows End Pt VC because it is connected via Segue. However when I select other tabs I manually try to push EndPt VC as shown in the tab controller method. But it shows blank screen. How can I present the same End Pt VC irrespective of tab selection?
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if let viewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "EndPointTVC") as? EndPtListTableViewController{
viewController.selectedTab = self.selectedTab
if let navigator = self.navigationController {
navigator.pushViewController(viewController, animated: true)
}
}
}

You can detect when the UITabBarController changes tab via its delegate UITabBarControllerDelegate. In the following delegate method, you can cast the selected view controller to a UINavigationController (which is what I can make out in the screenshot you attached).
extension MyTabBarController: UITabBarControllerDelegate {
func tabBarController(_ tabBarController: UITabBarController, didSelect viewController: UIViewController) {
let myVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "MyViewControllerStoryID") as! MyViewController
let navController = viewController as! UINavigationController
navController.viewControllers = [myVC]
print(viewController)
}
}
Now, no matter which tab you choose, a new MyViewController instance is created. Even hitting the same tab multiple times will replace the current instance for a new MyViewController instance.

Try to instantiate the viewcontroller from the storyboard and then present it. Also, make sure that you have given the target viewcontroller the storyboard Id "EndPointTVC" in the Identify section.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let viewController = storyboard.instantiateViewController(withIdentifier : "EndPointTVC")
viewController.selectedTab = self.selectedTab
self.present(viewController, animated: true)

Related

How to push a new view controller from a view

I created a view in .xib. On it, I placed a button on which I want to open another ViewController. I assigned a storyboardID to this ViewController. But I can't push the new controller, how can this problem be solved?
My code:
class MyView: UIView {
...
#IBAction func buttonPressed(_ sender: Any) {
let storyboard = UIStoryboard.init(name: "ViewController", bundle: Bundle.init())
let vc = storyboard.instantiateViewController(withIdentifier: "VC") as! ViewController
let navigationController = UINavigationController.init()
navigationController.pushViewController(vc, animated: true)
}
...
}
class ViewController: UIViewController {
...
}
Xcode gives this error:
'Could not find a storyboard named' ViewController'
but I checked there are no errors. I reloaded Xcode - no result.
Seems you have more than one issue.
You are trying to push a UIViewController into a non presented UINavigationController
You are creating an instance from a UIStoryboard with wrong identifier
First you need to choose the correct UIStoryboard which I am assuming it's main, then you have to present the UINavigationController from the holder UIViewController or trying to push from that UIViewController if it's has a UINavigationController,
Part 1: Fixing the instances,
let storyboard = UIStoryboard.init(name: "main", bundle: Bundle())
let vc = storyboard.instantiateViewController(withIdentifier: "VC") as! ViewController
let navigationController = UINavigationController(rootViewController: vc)
Now you have a navigationController that holds a your VC
Part 2: You want to present this navigation from your holding VC you can either add a listener to the UIButton from that VC and present/push from there
so lets wrap this up create a function in your VC that holds the UIView
#objc func pushToMyVc() {
let storyboard = UIStoryboard.init(name: "main", bundle: Bundle())
let vc = storyboard.instantiateViewController(withIdentifier: "VC") as! ViewController
let navigationController = UINavigationController(rootViewController: vc)
}
And in your UIView add this variable var selector: (() -> Void)?
Now in #IBAction func buttonPressed call this function selector?()
Now all we need is to pass what function I want to use when this button gets clicked in our case the function pushToMyVC that we put in our main UIViewController so in our didLoad or wherever you're calling this custom view
set the selector like this
myView.selector = pushtoMyVC
Side Note: you can't present/push UIViewController from non
UIViewController classes
Try changing how you fetch the storyboard from
let storyboard = UIStoryboard.init(name: "ViewController", bundle: Bundle.init())
to
let storyboard = UIStoryboard(name: "ViewController", bundle: nil)
note that the storyboard ID needs to be exactly ViewController.
If this still shows the same error, I'd say you have an issue with the storyboard target membership, so check that in the file inspector on the right side of the Xcode editor.
You should check your Storyboard file name again, because when you want to initiate a UIViewController from a Storyboard file, the name parameter in UIStoryboard initiation has to be exactly the same as the Storyboard file name.
#IBAction func buttonPressed(_ sender: Any) {
// Let's say that you have a Storyboard file named ViewController.storyboard and a UIViewController class named ViewController.swift
let destinationVC = UIStoryboard(name: "ViewController", bundle: nil).instantiateViewController(withIdentifier: "VC") as! ViewController
self.navigationController?.pushViewController(destinationVC, animated: true)
}
Hope this helps your issue
you should take a delegate back to viewController that contains current view, and call navigationController.push from there

Selected Index on Tab Bar not working on Swift

I want to change the default selected view controller of a UITabBar / UITabBarController via selectedIndex.
I tried other solutions I saw in other threads, like changing it in the AppDelegate or in ViewDidAppear like this:
class MainTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
setupTabBar()
}
override func viewDidAppear(_ animated: Bool) {
// this is the attempt to set the selected index that doesn't work
self.tabBarController?.selectedIndex = 2
}
func setupTabBar(){
let vc1 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "mainvc")
let vc2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "secondvc")
let vc3 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "thirdvc")
let FirstController = UINavigationController(rootViewController: vc1)
let SecondController = UINavigationController(rootViewController: vc2)
let ThirdController = UINavigationController(rootViewController: vc3)
viewControllers = [FirstController, SecondController, ThirdController]
}
}
Nothing worked so far, and it always shows the selected index = 0.
I set the tab bar view controller as the initial view controller in the Main storyboard. Should I be doing this in the AppDelegate instead?
You are saying that
class MainTabBarController: UITabBarController {
so self refers to a UITabBarController, but you are setting the selected index of tabBarController of self:
self.tabBarController?.selectedIndex = 2
There are no tab bar controllers embedded in self. self itself is a tab bar controller! You can just set self.selectedIndex:
self.selectedIndex = 2
Also, you should do that in viewDidLoad instead of viewDidAppear, because viewDidAppear can happen many times (every time some modal controller is dismissed for example). viewDidLoad will only be called once.

How to PopUp or Modally Present View Controller from Tab Bar Controller?

How can I create a popup that appears when a button on a Tab Bar is pressed? I would like something similar to this: https://www.youtube.com/watch?v=zDWSaItF2ko.
I have tried many solutions but none have worked.
For example, I have tried this with my main view controller:
This still doesn't work though. How would I go about creating this. I know that I need to present the view controller modally and over current context, but how would I do that from a tab bar controller.
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
if viewController is PopupViewController {
if let popupView = tabBarController.storyboard?.instantiateInitialViewController() {
popupView.modalPresentationStyle = .fullScreen
tabBarController.present(popupView, animated: true, completion: nil)
return false
}
}
return true
}
Here are some pictures that may help:
Main Storyboard:
Popup Storyboard:
View Controller Code:
Have you tried debug this using breakpoints in Xcode? From what I can see, the first thing you do is to check whether the view controller that should be selected is of class PopupViewController. Are you sure the view controller is being instantiated correctly?
And by the way I would recommend another way of instantiating the view controller from storyboard rather then:
tabBarController.storyboard?.instantiateInitialViewController()
The first thing is to head over to the storyboard file itself and in the view controller you are trying to instantiate change the Storyboard ID to something for example the class of the storyboard (PopupViewController in your case).
Next you will want to try to instantiate the storyboard itself using the init(name: String, bundle storyboardBundleOrNil: Bundle?) initialiser:
let storyboard = UIStoryboard(name: "Popup", bundle: nil)
And now instantiate the view controller using the storyboard variable like so:
let popupViewController = storyboard.instantiateViewController(withIdentifier: "PopupViewController") as! PopupViewController
Finally you can give it some extra configuration and present it on the tab bar controller:
popupViewController.modalPresentationStyle = .fullScreen
tabBarController.present(popupViewController, animated: true)
Edit
Addionally to make it more Swifty, I recommend guard statement for early exit. Finally the method could look something like this:
func tabBarController(_ tabBarController: UITabBarController, shouldSelect viewController: UIViewController) -> Bool {
guard viewController is PopupViewController else { return true }
let storyboard = UIStoryboard(name: "Popup", bundle: nil)
let popupViewController = storyboard.instantiateViewController(withIdentifier: "PopupViewController") as! PopupViewController
popupViewController.modalPresentationStyle = .fullScreen
tabBarController.present(popupViewController, animated: true, completion: nil)
return false
}

How can I show ViewController in UITabBarController?

I have a UITabBarController and all my other view controllers are connected to it. Now I want to show one my controller as:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: ViewController = storyboard.instantiateViewControllerWithIdentifier("ViewController") as! ViewController
but when I tried to:
let rootViewController = self.window?.rootViewController as! UINavigationController
rootViewController.pushViewController(vc, animated: true)
it gave me the next error:
Could not cast value of type 'UITabBarController' (0x1a899b818) to 'UINavigationController'
Later I've tried to do:
let rootViewController = self.window?.rootViewController as! UITabBarController
but in this case I get
UITabBar has no member pushViewController
How can I show/push my ViewController so it will appear with UINavigationBar and inside of UITabBar?
You need to place each of your view controllers inside a navigation controller.
E.g. currently you have a TabBarViewController
and two view controllers:
ViewControllerA
ViewControllerB
What you need to do is to embed each of them inside a navigation controller so you would have:
UINavigationController -> ViewControllerA
UINavigationController -> ViewControllerB
In order to push a new controller you would do:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc: ViewController = storyboard.instantiateViewControllerWithIdentifier("ViewController") as! ViewController
let navViewController = myTabBar.selectedViewController as? UINavigationController
navViewController?.pushViewController(vc, animated: true)
Could not cast value of type 'UITabBarController' (0x1a899b818) to 'UINavigationController'
Your root view controller is of type UITabBarController.
Therefore you have to use the appropriate methods of this class and not UINavigationController.
To set view controllers use either
var viewControllers: [UIViewController]? or
func setViewControllers([UIViewController]?, animated: Bool).
To have a navigation bar you have to instantiate a UINavigationController and add your view controller to this navigation controller.
Then add your navigation controller to your UITabBarController with via one of the above options.
If your class is UITabBarController
You can add this:
private func showViewController() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let vc = storyboard.instantiateViewController(withIdentifier: "queueTableViewController") as? QueueTableViewController else { return }
let navVC = self.selectedViewController as? UINavigationController
navVC?.pushViewController(vc, animated: true)
}
This will allow you to go to any VC
The second solution is something like this.
Here You will show the last VC and from there push some VC.
guard let tabCount = viewControllers?.count, tabCount > 1 else { return }
selectedIndex = tabCount - 1
if let navigationController = viewControllers?[selectedIndex] as? UINavigationController {
if let accountVC = navigationController.visibleViewController as? FirstViewController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let vc = storyboard.instantiateViewController(withIdentifier: "someViewController") as? SomeViewController else { return }
accountVC.navigationController?.pushViewController(vc, animated: true)
}
}

Presenting VC embedded in a NavController from AppDelegate

I'm trying to want to present/push a VC embedded in a NavController from AppDelegate. I previously used this code but somehow it's not working anymore:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let VC = storyboard.instantiateViewControllerWithIdentifier("PendingRequest") as! PendingRequestVC
let navController = UINavigationController.self(rootViewController: VC)
let rootViewController = UIApplication.sharedApplication().keyWindow!.rootViewController
rootViewController!.presentViewController(navController, animated: false, completion: nil)
Other codes open my desired VC but not within a navigation pane. Any guidance would be appreciated.
Calling from AppDelegate after user interacts with push notification.
Edit:
I'm able to present the right VC by using this code:
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
if let viewController = mainStoryboard.instantiateViewControllerWithIdentifier("PendingNavController") as? UINavigationController {
if let yourViewController = viewController.topViewController as? PendingRequestVC {
//yourViewController.getRequestdata()
}
UIApplication.sharedApplication().keyWindow!.rootViewController = viewController;
}
But this code won't allow me to go back using the Close button on my NavBar.
My structure is as follow:
TabController -> NavController1 -> VC1 -> NavController1a -> VC1a
I'm trying to get to VC1a and be able to use the Closed button to go back to VC1
Add a UIButton in your presented ViewController's View. Following event will be performed by that button. You can dismiss your Navigation Controller this way
#IBAction func dismissViewController(sender: AnyObject) {
self.navigationController.dismissViewControllerAnimated(false, completion:nil);
}

Resources