Hide view while push to tabBar - swift - ios

I have a view controller like below.
This view is attached with a tabBarController. The tabBarController has 5 viewControllers and I have to present the 5th viewController of tabBar from another page. So I used the below codes for present that viewController
#IBAction func onClickUserProfile(_ sender: Any) {
let navVc = self.storyboard?.instantiateViewController(withIdentifier: "ProfileVC")as! ProfileVC
navVc.userId = Int(self.userId)
navVc.navigationItem.hidesBackButton = true
navVc.tabBarController?.tabBar.isHidden = false
self.navigationController?.pushViewController(nxtVc, animated: true)
}
But after execute the code it resulting the view controller as the below image.
The view undergoes the tabBar. Anyone help me to push to a tabBar view.

You need to set the selected UIViewController from UITabBarController something like this should work .
self.tabBarController?.selectedViewController = self.tabBarController?.viewControllers![1]
where tabBarController?.viewControllers returns the array of current ViewControllers embedded in the UITabBarController .
Your code should be something like this.
#IBAction func onClickUserProfile(_ sender: Any) {
let vc = self.tabBarController?.viewControllers![1] as! ProfileVC // use your index
vc.userId = Int(self.userId)
self.tabBarController?.selectedViewController = vc
}
Note: Don't create an instance of the UIViewController as .instantiateViewController(withIdentifier:) use the already existed
ones in the array tabBarController?.viewControllers, creating new
instance will be treated as new one and gives the problem you have up
there .

Related

add navigation bar but there is no back button

I created a table view and from there let say a user pressed a cell it will go to ListTavleView but the only problem right now is that whenever a user is in ListTableView there is not back button even thought i already embed a navigation controller
and i want the fist view navigation bar is small title second view navigation bar is large title
enter image description here
Below is my code
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showList" {
if let indexPath = tableView.indexPathForSelectedRow {
let items = dataManager.items[indexPath.row]
let controller = (segue.destination as! UINavigationController).topViewController as! ListTableViewController
controller.item = items
controller.navigationItem.leftItemsSupplementBackButton = true
}
}
}
Below is my storybord setup
Navigation bar with no back button
From the image it seems that view controller is added as a child view controller in current view controller.
There is not need to embedded navigation controller when a cell is pressed becoz there is already a navigation controller at start point so no need to create a new one.(If you present a view controller then you may need to embed navigation controller.)
So the solution is...
Delete the navigation controller.
Connect directly to the destination view controller without navigation controller as there is already.
it is better if you use pushViewController, just get a reference of the other view controller, it will always a back button since you are pushing threw navigation Controller here is a simple example:
let story = UIStoryboard(name: "Main", bundle: nil)
let vc = story.instantiateViewController(withIdentifier: "ExampleViewController") as! ExampleViewController
self.navigationController?.pushViewController(vc, animated: true)
as for the back button, the issue is with your hierarchy.
are you changing the left item of navigation bar in another view controller that might affect navigation bar in your destination view controller.
You are pushing new NavigationController(say Nav.B) to the existing one(Nav.A).
Each navigation controller keeps different navigation stack. The back button is visible when you add viewcontroller to Navigation controller. Read more about UINavigationController.
For your current scenario, you could delete the second navigation controller(i think it not essential) & connect direct segue to ListTableViewController
So this
let controller = (segue.destination as! UINavigationController).topViewController as! ListTableViewController
becomes
let controller = segue.destination as! ListTableViewController
When you need large titles(available 11+), you can add this line in viewDidLoad()
navigationController?.navigationBar.prefersLargeTitles = true
And if it needed only for this Viewcontroller, add in viewWillDisappear() or viewDidDisappear()
navigationController?.navigationBar.prefersLargeTitles = false
If you wanted to have navigation bar back button on next view, then just push the target view on navigation, it will show default navigation back button. No, need to any extra work.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "showList" {
if let indexPath = tableView.indexPathForSelectedRow {
let items = dataManager.items[indexPath.row]
guard let controller = segue.destination as? ListTableViewController else {
return
}
controller.item = items
self.navigationController?.pushViewController(controller, animated: true)
}
}}
And if you are pushing the viewcontroller with segue, then no need to add below line self.navigationController?.pushViewController(controller, animated: true)

Can pass data between View Controllers but Tab bar disappears

I have a Tabbed App with two tabs... the first tab has the main Action, and the second has Settings that can be updated. I am trying to pass some variables data from Settings to the Action tab. Based on some suggestions, I have used the following code for the Update button:
#IBAction func updateBut(_ sender: Any) {
let myVC = self.storyboard?.instantiateViewController(withIdentifier: "FirstViewController") as! FirstViewController
myVC.totTime = totalTime
myVC.timeInt = intTime
self.present(myVC, animated: true, completion: nil)
}
The data does pass to the first view controller, however, the tabs have disappeared on this view now. So, how can I get the tabs back on the screen? I am quite the beginner to any form of app development, and am just trying to learn by doing... the Tabbed App has been created using one of the Xcode New Project templates. Thanks.
try this way
(self.tabBarController?.viewControllers?[0] as! FirstViewController).totTime = totalTime
(self.tabBarController?.viewControllers?[0] as! FirstViewController).timeInt = intTime
self.tabBarController?.selectedIndex = 0
self.tabBarController?.tabBar.isHidden = false try this
A much better way to pass data by using protocols, you can define a protocol like below
protocol PassDataDelegate{
func updateFirstVc(totalTime:String)
}
and in your SecondViewController class have a delegate property like below
class SecondViewController:UIViewController{
myDelegate:PassDataDelegate?//declaration part
#IBAction func updateBut(_ sender: Any) {
myDelegate.updateFirstVc(totalTime:totalTime)
}
}
And finally in your UITabController class implement the protocol
class myTabController:UITabController,PassDataDelegate{
var firstController:FirstViewController? //declaration part
var secondController:SecondViewController?
override func viewDidLoad(){
super.viewDidLoad()
//initialize your view controller here
self.firstViewController = FirstViewController()
self.secondViewController = SecondViewController()
self.secondViewController.myDelegate = self //assign delegate to second vc
self.viewcontrollers = [firstController, secondController]
}
updateFirstVc(totalTime:totalTime){
self.firstViewController?.totTime = totalTime
self.selectedIndex = 0// here change the tab to first vc if you want to switch the tab after passing data
}
}
I am new to iOS correct me If I am wrong,
The reason why the TabBar is not visible since you are instantiating new FirstViewController which is present on top of your TabBar.
TabBar by default does this Job or Add the new viewController to the TabBar Stack.
tabBarController.viewControllers.append(myVC)
For passing the data TabBar holds the reference of all its ViewControllers. So you can set or get in each other ViewControllers like this
var yourData{
set{
let yourVC = self.tabBarController?.viewController[0] as? FirstViewController ?? ErrorClass
yourVC.dataObj = newValue
}
get{
let yourVC = self.tabBarController?.viewController[0] as? FirstViewController ?? ErrorClass
return yourVC.dataObj
}

Calling function with argument from segue

I have a FirstVC with table view with segue to another SecondVC where i pass the name of pressed cell. In SecondVC i have another tableview which i want to populate with data. I have a function called gettingUrls() which is pulling data for my table view:
DataService.instance.mainRef.child("Folders").child("\(folderName)").observeSingleEvent(of: .value) { (snapshot: DataSnapshot) in...}
where "folderName" is the value from FirstVC. This function is implemented into "pull to refresh feature" and it works great. However when i try to run the function itself in viewDidLoad it keeps crashing the app because of empty folderName value. I just can't understand why at this point the value from segue is available in refresh function and not in viewDidLoad.
Here is the code of segue:
let detailVC = self.storyboard?.instantiateViewController(withIdentifier: "DetailVC") as! DetailVC
self.navigationController?.pushViewController(detailVC, animated: true)
self.present(detailVC, animated: true, completion: nil)
detailVC.folderName = fileName
How can i run the function in viewDidLoad?
You're presenting the DetailVC's view before you've set its folderName property. The view has loaded but it doesn't yet have a folderName so the program crashes. You should place the line detailVC.folderName = fileName before you present the DetailVC.
You also don't need both the 2nd and 3rd lines - either present the new view controller or push it onto the navigation stack, not both.
You should write code with segues to push viewControllers like following not with navigationController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "Verification") {
let vc = segue.destination as! MyViewController
vc.delegate = self
vc.property = value
}
}
So with #haarismuneer help, it was enough to pass the variable before initializing SecondVC:
detailVC.folderName = fileName
self.navigationController?.pushViewController(detailVC, animated: true)

Trigger segue in another UITabBarController VC with payload?

I can swap from one UIViewController to another within a UITabBarController using tabBarController?.selectedIndex = targetIndex. I would like to know how to trigger a segue after the switch.
I have tried doing if let vc = tabBarController?.viewControllers?[0] as MyViewController {} and calling vc.performSegue() but it errors out, complaining that it's a UINavigationController. (I couldn't even get vc.prepare() to work but I can't work out how to create a UIStoryBoardSegue, which it requires as the first argument, from scratch using the segue identifier.) I think this is because all of my UIViewControllers in the UITabBarController are within Navigation Controllers.
How do I switch from index 0 to 1 in the tab bar controller, and then trigger a segue in the view controller embedded in the navigation controller? Note, I need to pass in a copy of the calling object (my UIViewController) or a payload for contextual purposes to the UIViewController being segued to.
Well like you said the viewController which is presented at the index is a UINavigationController. You should get the rootViewController of the UINavigationController and perform the Segue on the RootViewController.
Below code should work:
self.tabBarController?.selectedIndex = 1
guard let vc = self.tabBarController?.viewControllers?[1] as? UINavigationController else {
return
}
guard let rootVC = vc.viewControllers.first as? SecondViewController else {
return
}
rootVC.vc = self
rootVC.performSegue(withIdentifier: "test", sender: self)

Swift Navigating to ViewController thats 3 windows deep

I have a ViewController that I want to present when a push notification is opened from the locked screen.
The view controller is embedded inside a chain of view controllers, the chain is:
TabbedBarController(root) -> NavigationController0 -> ViewController0 -> ViewController1 -> ViewControllerIWant
I have two variables from the push notification I want to pass into ViewControllerIWant
So far I have:
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let rootVC = storyboard.instantiateViewControllerWithIdentifier("HomeVC") as! UITabBarController // HomeVC is the TabbedBarController
let NaviVC = storyboard.instantiateViewControllerWithIdentifier("NavigationController0") as! UINavigationController // NavigationController0 is the Navigation Controller for this stack
let ViewController0 = storyBoard.instantiateViewControllerWithIdentifier("ViewController0") as ViewController0
let ViewController1 = storyBoard.instantiateViewControllerWithIdentifier("ViewController1") as ViewController1
let ViewControllerIWant = storyBoard.instantiateViewControllerWithIdentifier("ViewControllerIWant") as ViewControllerIWant
// Pass the Variables into ViewControllerIWant
ViewControllerIWant.Variable0 = "This is the first Variable"
ViewControllerIWant.Variable1 = "this is the second variable"
// Load the VC
self.presentViewController(ViewControllerIWant, animated:true, completion:nil)
I can load ViewControllerIWant directly but then loose the navigation and tab controllers so moving backwards requires you yo close the app and reload.
How do I nest the VC's so that when the user swipes this notification it loads ViewControllerIWant ?
You can just set the viewControllers of the navigationController. And show the current navigationController.
In your example you should add this after the line will be (I have change all to a lowercase. You really shouldn't be using a uppercase as variable)
viewControllerIWant.variable1 = "this is the second variable"
naviVC.viewControllers = [viewController0, viewController1, viewControllerIWant]
You can keep your navigation controller by using segues instead of presenting the View Controller
Once the segue is created from your starting VC into your desired VC, you give it an identifier (e.g. "segueFromVC1ToVCIWant").
Then call the method as an action of your notification:
self.performSegueWithIdentifier("segueFromVC1ToVCIWant", sender: nil)
Finally can pass the variables like this:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if(segue.identifier == "segueFromVC1ToVCIWant"){
let viewControllerIWant = segue.destinationViewController as! ViewControllerIWant
ViewControllerIWant.variable0 = self.variable0
ViewControllerIWant.variable1 = self.variable1
}
}
Make sure you create variables with the same name in ViewControllerIWant in order to receive the values.

Resources