How to programmatically open certain tab of a view controller - ios

I am writing a code in which I have a view controller with certain tabs. Upon the dismissal of a an alert, I want open 2nd tab automatically. For this purpose I posted a local notification when the alert this dismissed. The observer for this notification is in home view controller, when this observer is called, 2nd tab of the view controller is selected. Here is my code for a better understanding:
vc.dismiss(animated: true, completion: {
// vc is the view controller in which my custom alert is shown
NotificationCenter.default.post(name: NSNotification.Name.OpenConsumerTab,
object: ConsumerHomeTab.Stats.rawValue)
})
And home view controller: (question related code)
NotificationCenter.default.addObserver(self,
selector: #selector(onOpenConsumerTabNotificationRecieved(notification:)),
name: Notification.Name.OpenConsumerTab,
object: nil)
#objc public func onOpenConsumerTabNotificationRecieved(notification: Notification) {
if (notification.object as! Int == ConsumerHomeTab.Stats.rawValue)
{
selectedIndex = 1
}
}
This is opening the tab but the output I am obtaining is this: (getting an extra black bar)
Why is this happening? Maybe it is showing up a completely new home view controller (with tab bar) but what is the reason? Correct me if I am wrong as I am newbie to iOS.
Things I have tried:
Get the root view controller (home view controller) and select its
tab but the output was same.
Using pop view controller but it opens up first tab (index 0) but my
requirement is to open the second tab (index 1)

I think there is no need of using Notification center too, u can write this code when you are dismissing that VC
to dismiss your alert
alert.dismissWithClickedButtonIndex(-1, animated: true)
to change View-Controller:
self.tabBarController?.selectedIndex = selectedTabIndex
// selectedTabIndex is the index of ViewController which you want to select

Please use tabbarController instance to set selected index as, may be you not using tabbar controller instance to set index :
if let tababarController = self.window!.rootViewController as! UITabBarController? { tababarController.selectedIndex = 1
}

Related

Navigation bar won't show after pushing a view controller

I'm trying to move from a View Controller to another.
I worte this function to use when the user tap on a button to move to the new view controller:
#objc private func infoButtonTap(){
let navVC = UINavigationController()
navVC.addChild(AboutViewController())
self.navigationController?.pushViewController(AboutViewController(), animated: true)
}
The problem is that the new view controller is presented on the screen but I don't have a navigation bar and a back button to move back.
I do not use Storyboard as I want to learn coding the UI.
I tried few things I found here on Stackoverflow but none worked for me.
How can I set the new view controller to have a navigation bar with back button?
UINavigationController has a variable isNavigationBarHidden
#objc private func infoButtonTap(){
let navVC = UINavigationController()
navVC.addChild(AboutViewController())
self.navigationController?.isNavigationBarHidden = false
self.navigationController?.pushViewController(AboutViewController(), animated: true)
}
You need to push the view Controller. Try this
let aboutVC = AboutVC()
self.navigationController?.pushViewController(aboutVC, animated: true)
You don't need to write any code.
Select the Root Navigation Controller the will control the app. In the Inspector Bar, select Simulated Metrics ( The Third Selection from the Right in the Inspector) and Check the Box " Is Initial View Controller". Then connect the next View controller which will be in essence the Landing page for the app. Once you connect other View Controllers to that View controller via a button for instance ( Select the button, then press Control Key + Drag to View Controller, select Show) , you will see the navigation Bar with "Back" displayed. Once that's done, you can add other view controllers and connect them from the landing page view controller and the Navigation Bars will be displayed.
For navigation to be visible and of use in an app , first you need to set up a Navigation controller with a Root view controller i.e your first controller and from there you can use push method on your navigation controller object to push a controller on to the stack.
For eg
let navVC = UINavigationController.init(rootViewController: YourFirstViewControllerObject())
navVC.pushViewController(NewViewControllerObj(), animated: true)

How can I push two VC but only presenting top VC

I would like to emulate contact list in ios. When one taps + button to add contact, a new view controller is presented that gives you textfields to enter contact name and additional info for saving about that contact. Once you tap Done button, the next view controller presented already seems to be embedded in a navigation controller with the button that takes you back to the contact list. I've tried code found on here but it pushes 3 view onto navigation stack
//first attempt
var controllers = navigationController?.viewControllers
controllers?.append(secondVc)
controllers?.append(thirdVC)
navigationController?.setViewControllers(controllers!, animated: true)
//second attempt
let pushVC = UIViewController()
let backVC = UIViewController()
if let navigationController = navigationController {
navigationController.pushViewController(pushVC, animated: true)
let stackCount = navigationController.viewControllers.count
let addIndex = stackCount - 1
navigationController.viewControllers.insert(backVC, atIndex: addIndex)
}
I've also tried other combinations that look cringy. Here is what I want it to look like: https://imgur.com/a/IAJ5G
This should work:
navigationController.pushViewController(secondVc, animated:false)
navigationController.pushViewController(thirdVc, animated:true)
Apple does the same by presenting a new window, with the form to create a new contact.
After making the new window as the keyWindow, they push the contact details VC on to the navigation controller on the original window. This push happens in the background, which the user is not able to witness.
You can view the same by attaching a debugger to the Contacts app.
Heres a screenshot of the view hierarchy. You will be able to see that there are no view controllers from the original window underneath the CNContactContentViewController.
When the user taps Done, the original window is restored as the keyWindow, and the contact details VC is updated to show the newly added contact.

can we programatically set to which tabar item should be displayed when a tab bar is shown?

I am working on a project which contain a tab bar. The tab bar contain 2 items named leave and Od. Both are normal viewcontroller class. They have a table view inside it(I dont mean table view controller). While clicking on a item in a table view a pop Up screen appears which have a viewcontroller swift file named as popUpviewController. This show details of item selected in table view. The problem is when i dismiss the popUpdialog i always get the selected tabBar item as the default one.Here its leave authorise.
what I did is i gave an Storyboard Id to tab Bar controller and called it from the popUp when its dismissed, like this.
let viewController:UIViewController = UIStoryboard(name:self.whichSB!, bundle: nil).instantiateViewControllerWithIdentifier("AuthoriseTabBar?") as UIViewController
self.tabBarController?.selectedIndex=2 /* DOESN'T WORK OBVIOUSLY*/
self.presentViewController(viewController, animated: false, completion: nil)
** The tab bar controller doesn't have any associated class with it.I would like to show item 1 when item1 popUp is dismissed(This works as its now the default item shown in tab bar), and item 2 when item 2 popUp is dismissed.**
Can anyone suggest a away of doing the above . and I havent used any navigation controller here, is it necessory to get tabbar.selectedindex of tab bar
You can access the tab bar, from the popup VC, like this. Just run this with the normal dismiss line as shown.
if let presentingVC = self.presentingViewController {
if let tabController = presentingVC.tabBarController {
tabController.selectedIndex = 0 // Whatever index you want to select.
}
}
self.dismiss(animated: true, completion: nil)

How to push new view in detail view controller from master view controller?

I have been scratching my head trying to figure out how to do this. If anyone has some insight it would be greatly appreciated. I've attempted to do this using segues and push/presentViewController methods. With pushViewController nothing happens.
Scenario: Split view controller has two navigation controllers connected (one as master, one as detail). The master's navigation controller has a form with various cells that should control what is being displayed in the right hand side detail view when in landscape mode on the iPad. The navigation controller connected to the detail view has storyboard references connected to it (3 of them).
What I want to do: From the master view controller (which is the app menu), I would like to control what is being displayed in the detail view while maintaining navigation bar.
Attempt 1:
let detailVC = self.splitViewController!.viewControllers[1]
let newVC = UIStoryboard(name: "D", bundle: nil).instantiateViewControllerWithIdentifier("P")
detailVC.self.navigationController?.pushViewController(newVC, animated: true)
Attempt 2:
let detailVC = self.splitViewController!.viewControllers[1]
let newVC = UIStoryboard(name: "D", bundle: nil).instantiateViewControllerWithIdentifier("P")
detailVC.performSegueWithIdentifier("navP", sender: self)
One other related question I had...if a user does many hops between several of the menu options, how can one "reset" the back button's history in the navigation bar to prevent a case where clicking back will cycle you through the same several views?
You shouldn't be pushing the view controller or performing a segue but calling showViewController.
Do you definitely need to maintain the navigation bar or can you show different UINavigationBars (via showing your view controller embedded in an UINavigationViewController potentially)?
Alternatively just show a single view controller and use the logic you add in your view controller to change the content under the control of your master view controller.
After some experimenting with the best approach I solved this. :) Solution below for all devices (iPhone/iPad).
Define extension for UISplitViewController:
Modified version based off https://stackoverflow.com/users/4418308/santiago-bendavid
extension UISplitViewController {
func toggleMasterView() {
if UIScreen.mainScreen().bounds.height > UIScreen.mainScreen().bounds.width {
var nextDisplayMode: UISplitViewControllerDisplayMode
switch(self.preferredDisplayMode){
case .PrimaryHidden:
nextDisplayMode = .AllVisible
default:
nextDisplayMode = .PrimaryHidden
}
UIView.animateWithDuration(0.2) { () -> Void in
self.preferredDisplayMode = nextDisplayMode
}
} else {
// do nothing
}
}
}
Code in master navigation controller's root view controller:
let newVC = UIStoryboard(name: "some_storyboard_id", bundle: nil).instantiateInitialViewController()
if self.splitViewController!.viewControllers.count == 2 {
let detailVC = self.splitViewController!.viewControllers[self.splitViewController!.viewControllers.endIndex - 1]
if detailVC.childViewControllers[detailVC.childViewControllers.count - 1].restorationIdentifier! != "some_id_here" {
detailVC.childViewControllers[0].navigationController?.pushViewController(newVC!, animated: true)
}
self.splitViewController!.toggleMasterView()
self.navigationController?.splitViewController!.preferredDisplayMode = .Automatic
} else {
self.navigationController?.pushViewController(newVC!, animated: true)
}
This code works on regular iPhone (non-Plus) where the master view controller is the sole controller used for navigation. On iPads and iPhone 6/6s+ models (which behave same as an iPad) I check whether the current view is already present using the restoration ID property, and then present the new view if it's not the same as the one already rendered on screen and dismiss the master view controller if we're in portrait mode. If in landscape, we keep it on screen (default behavior).

How can I segue or pop back to the second scene of the first tab in a tabbed app, from a scene on another tab?

I have a storyboard set up with a tab bar controller and three tabs. Each tab has a navigation controller. The first tab has three scenes. There is a button (log out) in a view on the third tab that I would like to segue to the second scene on the first tab (corresponding to the log in view controller and connected to the first scene via Show(e.g., Push).
Here is what I've tried:
self.tabBarController?.selectedIndex = 0
This works, insofar as I get back to the first tab's initial scene after tapping the UIButton. But since I want to get to the second scene, this is not a complete solution. I think the solution may be along the lines of:
self.tabBarController?.selectedViewController = LoginViewController()
or
self.tabBarController?.setViewControllers(self.LoginViewController, animated: true)
But I do not want to create another instance of a view controller.
Can I still use .selectedIndex to implement a solution?
A simple solution u can try is
1. Set a Global variable (i.e in App Delegate) name as isLogoutClick of type boolean.
2. While you are on third tab and click on logout button then make the global variable "isLogoutClick" as true.
3.and then navigate to first tab (1st scene) and on viewDidLoad just check the condition that
if(appDelegate.isLogoutClick)
{
push your view to next scene.
}
4. make false the value of isLogoutClick.
5. make sure at initially the value of isLogoutClick is false.
try this might it will help you.
After setting selectedIndex to 0, perform the segue you want (in this example, "loginSegue"). You can name your segue in the storyboard if you haven't already.
tabBarController?.selectedIndex = 0
if let someViewController = tabBarController?.viewControllers?[0] as? SomeViewController {
someViewController.performSegueWithIdentifier("loginSegue", sender: nil)
}
I'm not sure if this works for tabBarController because I've used this for my navigationController but should work the same.
if let tab = self.tabBarController?.viewControllers {
if let index = find(tab.map { $0 is LoginViewController }, true) {
let destination = tab[index] as LoginViewController
tabBarController?.presentViewController(destination, animated: true, completion: nil)
}
}
With a navigationController I would use popToViewController but I'm not sure how the tabBarController exactly works

Resources