Tab Bar Controller disappearing when moving back to another view - ios

I want to create an app that lets the user click on a button on V1 on the navigation bar. This will segue to V2 where they can press another button on the navigation bar on V2 and it will bring them back to V1 which has the tab bar controller at the bottom. I don't want the tab bar on V2 and I don't want V2 as a tab bar item. When I try this the tab bar disappears on V1 when I segue back to V1 from V2.
TAB BAR CONTROLLER -> TAB BAR ITEM (V1) -> V2(Via navigation bar button item on V1) -> back to V1(Via navigation bar button item on V2)
I have added [self.navigationController, popViewControllerAnimated:YES]; to my button function but it comes up with an error - expected expression in container literal.
Apart from this code I have not got any other code in my app yet.
I am using Xcode 8.0 and Swift 3.0

First create subclass of UITabBarController, Then add properties to AppDelegate
var navController: UINavigationController?
var tabController: MyTabController?
If you want to show tab bar controller on app launch then put these code in AppDelegate in didFinishLaunchingWithOptions
self.window = UIWindow(frame: UIScreen.main.bounds)
let myStoryboard = UIStoryboard(name: "Main", bundle: nil) as UIStoryboard
self.tabController = myStoryboard.instantiateViewController(withIdentifier: "MyTabController") as? MyTabController
//self.navController = UINavigationController(rootViewController: self.tabController!)
//self.window?.rootViewController = self.navController
self.window?.rootViewController = self.tabController
self.window?.makeKeyAndVisible()
return true
If you want to jump on tab bar after login or something else then , add property to that controller
var appDelegate: AppDelegate!
in viewDidLoad
appDelegate = UIApplication.shared.delegate as? AppDelegate
And method should like
func logIntoApp() {
appDelegate.tabController = self.storyboard?.instantiateViewController(withIdentifier: "MyTabController") as? MyTabController
appDelegate.window?.rootViewController = appDelegate.tabController
}
Then in your tab item view controller , create property of AppDelegate and assign delegate as above.
And methods should be like :
#IBAction func showWithTab(_sender: AnyObject) {
let DefaultVC = self.storyboard?.instantiateViewController(withIdentifier: "DefaultViewController") as! DefaultViewController
self.navigationController?.pushViewController(DefaultVC, animated: true)
}
#IBAction func showWithoutTab(_sender: AnyObject) {
let DefaultVC = self.storyboard?.instantiateViewController(withIdentifier: "DefaultViewController") as! DefaultViewController
// You can create your own animation
UIView.transition(from: (appDelegate.tabController?.view)!, to: (appDelegate.navController?.view)!, duration: 0.3, options: UIViewAnimationOptions.curveEaseIn) { (finished) in
self.appDelegate.window?.rootViewController = self.appDelegate.navController
}
// OR you can use like this way
UIView.transition(from: self.view, to: DefaultVC.view, duration: 0.3, options: UIViewAnimationOptions.curveEaseIn) { (finished) in
self.appDelegate.window?.rootViewController = self.appDelegate.navController
}
}

Why not use a simple Tab Bar?
Something like this:
and in VC1 :
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
myTab.removeFromSuperview()
}

I have solved my problem after days of trying to figure out an answer. You need to add this to any view controller.
#IBAction func unwindToViewController (sender: UIStoryboardSegue){
}
Then you can add a segue from bar button item to the exit icon on the view controller.
You can view an image of the VC scene here.

Related

How to present a ViewController with a Navigationbar programatically?

When presenting my viewcontroller this way I don't get a navigationbar, even though the viewcontroller is embedded in a navigation controller in the interface builder.
if let stockVC = storyboard?.instantiateViewController(withIdentifier: "Stock") as? StockTableViewController {
stockVC.stockToDisplay = commonData.stock.filter { $0.productID.contains(product) }
// No NavigationBar with this one
navigationController?.present(stockVC, animated: true)
// No NavigationBar with this one
self.present(stockVC, animated: true)
}
I know the navigationBar works, because if I set the ViewController as initial in the storyboard, it shows.
What am I doing wrong here?
When you present a view controller, it is not part of the current navigation stack. You need to create a new navigation controller with your stockVC contained in it, and present the new navigation controller:
if let stockVC = storyboard?.instantiateViewController(withIdentifier: "Stock") as? StockTableViewController {
stockVC.stockToDisplay = commonData.stock.filter { $0.productID.contains(product) }
let stockNavigationController = UINavigationController(rootViewController: stockVC)
self.present(stockNavigationController, animated: true)
}

Side bar menu icon is invisible in Tab bar based view controller

I've implemented side bar using SWRevealViewController. But when i do same using Tab bar based view controller, menu icon is invisible only in tab based screens.
Icon is visible in interface builder but invisible in simulator/device.
Functionality is working in every screen, even in tab based view controller.
When i drag from left to right and vice versa, functionality is working fine.
Only the issue is menu icon is invisible in simulator.
Can anyone help me?
Thanks in advance.
below is the swift file for tab
import UIKit
class ItemOneViewController: UIViewController {
#IBOutlet weak var menuBar: UIBarButtonItem!
override func viewDidLoad() {
super.viewDidLoad()
setMenuBarBtn(menuBar: menuBar)
navigationController!.navigationBar.barTintColor = UIColor.color(.blue)
}
func setMenuBarBtn(menuBar: UIBarButtonItem) {
menuBar.target = revealViewController()
menuBar.action = #selector(SWRevealViewController.revealToggle(_:))
view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
}
func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.portrait
}
}
Code is similar for three tabs.
I've done below tasks before asking question here
Deleted and recreated screens two times.
Cross checked everything and compared tab bar based scenes source code with other scenes.
googled it.
Below are the images for your reference
Tab based screen with side bar functionality (working good)
Tab based screen without menu icon (Problem persist here , i want menu icon to be displayed)
HomeScreen with menu icon (Working good)
Navigation Controller attribute inspector
my storyboard
Item One View Controller view hierarchy
Item One view Controller related navigation controller scene
You gave to add normal button on HomeViewController
Then directly perform segue through storyboard(from burger menu button).
#IBAction func sideMenuBtnAction(_ sender: UIButton) {
performSegue(withIdentifier: "sideMenu", sender: self)
}
Then on SideMenuController :
import UIKit
import SideMenu
class SideMenuViewController: UIViewController {
#IBOutlet var sideMenuTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
SideMenuManager.default.menuPresentMode = .menuSlideIn
SideMenuManager.default.menuFadeStatusBar = false
SideMenuManager.default.menuWidth = self.view.frame.width / 1.4
SideMenuManager.default.menuShadowOpacity = 0.3
sideMenuTableView.tableFooterView = UIView(frame: .zero)
}
}
You need to create a tabbarController class like below, and assign this ViewController class to tabbar on storyboard ...
I have not tested this code but something like this should work for your case
class ViewController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
let firstSw = setRootViewControllerFor(identifier: "firstViewController") as! SWRevealViewController
let secondsw = setRootViewControllerFor(identifier: "secondViewController") as! SWRevealViewController
let thirdSw = setRootViewControllerFor(identifier: "thirdController") as! SWRevealViewController
self.viewControllers = [firstSw,secondsw, thirdSw]
// Do any additional setup after loading the view.
}
func setRootViewControllerFor(identifier:String)->SWRevealViewController
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sw = storyboard.instantiateViewController(withIdentifier: "sw") as! SWRevealViewController
self.view.window?.rootViewController = sw
let destinationController = self.storyboard?.instantiateViewController(withIdentifier: identifier)
let navigationController = UINavigationController(rootViewController: destinationController!)
navigationController.navigationBar.isHidden=false
navigationController.setNavigationTints() //call your UI method to set
sw.setFront(navigationController, animated: true)
return sw
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destination.
// Pass the selected object to the new view controller.
}
*/
}
OR Following by doing this there is no need to create a tabbarController
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// create UIWindow with the same size as main screen
window = UIWindow(frame: UIScreen.mainScreen().bounds)
// create story board. Default story board will be named as Main.storyboard in your project.
let storyboard = UIStoryboard(name: "Main", bundle: nil)
// create view controllers from storyboard
// Make sure you set Storyboard ID for both the viewcontrollers in
// Interface Builder -> Identitiy Inspector -> Storyboard ID
let firstSw = setRootViewControllerFor(identifier: "firstViewController") as! SWRevealViewController
let secondsw = setRootViewControllerFor(identifier: "secondViewController") as! SWRevealViewController
let thirdSw = setRootViewControllerFor(identifier: "thirdController") as! SWRevealViewController
// Set up the Tab Bar Controller to have two tabs
let tabBarController = UITabBarController()
tabBarController.viewControllers = [firstSw,secondsw, thirdSw]
// Make the Tab Bar Controller the root view controller
window?.rootViewController = tabBarController
window?.makeKeyAndVisible()
return true
}
func setRootViewControllerFor(identifier:String)->SWRevealViewController
{
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let sw = storyboard.instantiateViewController(withIdentifier: "sw") as! SWRevealViewController
self.view.window?.rootViewController = sw
let destinationController = self.storyboard?.instantiateViewController(withIdentifier: identifier)
let navigationController = UINavigationController(rootViewController: destinationController!)
navigationController.navigationBar.isHidden=false
navigationController.setNavigationTints() //call your UI method to set
sw.setFront(navigationController, animated: true)
return sw
}

Present UINavigationController from inside UITabBarController

I have a quick question regarding instantiating a particular view controller from my AppDelegate. I am building a simple chat app that needs to respond when a user opens the app via a remote notification. The desired behavior of my app is that when the user taps this notification the application will open to that particular chat screen.
The basic flow of the app is as follows:
The highest level UI component is a UITabBarController
Inside this tab bar is a navigation controller
Inside the nav controller is a view controller (let's call it the "list") which lists all the available chat windows
Once a user taps on one of the rows of my tableview that lists the chats a new view controller is pushed. This view controller is the chat window which automatically hides the tab bar but keeps the back button of the nav controller
When the user taps the back button it will return to the chat list and the tab bar will reappear
I am using the following code to get the correct chat screen to appear. I can tap the back button and it will return to the chat list as expected. The only problem is that the navigation controller isn't embedded in the tab controller. This code is being called from func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler:
let mainStoryboard : UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let initialVC : UIViewController = mainStoryboard.instantiateViewController(withIdentifier: "ChatVC") as UIViewController
let detailVC : UIViewController = mainStoryboard.instantiateViewController(withIdentifier: "ChatDetailVC") as UIViewController
let navContr = UINavigationController(rootViewController:initialVC)
self.window = UIWindow(frame: UIScreen.main.bounds)
self.window?.rootViewController = navContr
self.window?.makeKeyAndVisible()
initialVC.navigationController?.pushViewController(detailVC, animated: true)
How can I configure my code so that the tab controller is the top level of the stack? It's labelled "TabController" in my storyboard.
You just need to create a flag which determine if user comes from notification tap or normally launch
struct NotificationEvent {
static var isFromNotification = false
}
The stack of controller is -
TabBarController->UINavigationController->ChatListViewController->ChatDetailViewController
In AppDelegate func userNotificationCenter -
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "TabViewController") as! TabViewController//Your Tabbarcontroller
NotificationEvent.isFromNotification = true//recognize is from notification
window?.rootViewController = vc
ChatListViewController
class ChatListViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if NotificationEvent.isFromNotification {
openChatDetailScreen()//Jumps to that chat screen
NotificationEvent.isFromNotification = false
}
}
#IBAction func tapsOnChatList(_ sender: UIButton) {//consider it as didselect method in your tableView
openChatDetailScreen()
}
func openChatDetailScreen() {
let vc = self.storyboard?.instantiateViewController(withIdentifier: "ChatDetailViewController") as! ChatDetailViewController
navigationController?.pushViewController(vc, animated: true)
}
}
Or instead of NotificationEvent.isFromNotification you can also check with UIApplicationLaunchOptionsKey.remoteNotification/observer of notification event in particular viewcontroller.

navigation through view controllers while skipping one - iOS

I have a view controller as my initial view controller.
there's a button in it(GO button) which when the user taps, it should go to another view controller(let's call it Destination view controller with label 'This is where i wanna go'). Meanwhile i want to pass it through a Tabbar controller. The reason is i want to have tabbar in my navigation stack and when users presses back on Destination view controller, it must go to tabbar controller. Picture shows what i want. what can I do to skip tabbar while having it in navigation stack?
You can do that easily inside the IBAction of GO button:
#IBAction func goTapped(_ sender: UIButton) {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc1 = storyboard.instantiateViewController(withIdentifier: "myTabBarViewController")
let vc2 = storyboard.instantiateViewController(withIdentifier: "myGoalViewController")
let controllers = [vc1, vc2]
self.navigationController!.setViewControllers(self.navigationController!.viewControllers + controllers, animated: true)
}
Good luck!
Going to DestinationViewController could be manually:
if let destinationViewController = self.storyboard?.instantiateViewController(withIdentifier: "Storyboard ID of DestinationViewController") {
self.navigationController?.pushViewController(destinationViewController, animated: true)
}
(Alternatively, you could make a segue from FirstViewController to the DestinationViewController directly in Storyboard)
And in your DestinationViewController, insert the TabbarController to the Navigation sequence manually after view did appear, then you are able to go back to the TabbarController:
class DestinationViewController: UIViewController {
//......
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if self.isBeingPresented || self.isMovingToParentViewController {
var viewControllers = self.navigationController?.viewControllers
if let index = viewControllers?.endIndex.advanced(by: -1),
let tabBarController = self.storyboard?.instantiateViewController(withIdentifier: "Storyboard ID of TabBarController") {
viewControllers?.insert(tabBarController, at: index)
self.navigationController?.viewControllers = viewControllers!
}
}
}
//......
}

How to configure SWRevealViewController Programmatically?

I am working on application where after registration process home screen will appear. And the SWRevealViewController has been configure in home screen.
I want to configure SWRevealViewController programmatically.
Below is my code
let storyboard = UIStoryboard(name: "MainSW", bundle: nil)
let vc = storyboard.instantiateViewControllerWithIdentifier("HomeViewController") as! HomeViewController
let rvc:SWRevealViewController = self.revealViewController() as SWRevealViewController
rvc.pushFrontViewController(vc, animated: true)
But it gives me error like below.
Could not cast value of type 'UINavigationController' (0x110d9f860) to
'SWRevealViewController' (0x10dc346b8).
I have tried below link also
SWRevealViewController solution
But it also doesn't help me.
Here is how I have configured it :
I have created 2 UIBarButtonItem as properties of the HomeViewController (I will set them in code)
let leftButton = UIBarButtonItem()
let rightButton = UIBarButtonItem()
In viewDidLoad method:
revealViewController().delegate = self
In viewWillAppear method:
if (revealViewController() != nil) {
revealViewController().rearViewRevealWidth = -50
revealViewController().rightViewRevealWidth = -50
leftButton.image = UIImage(named: "yourImageName")! // check that the image exists, don't use !
leftButton.style = .plain
leftButton.target = revealViewController()
leftButton.action = #selector(SWRevealViewController.revealToggle(_:))
rightButton.image = UIImage(named: "yourImageName")! // check that the image exists, don't use !
rightButton.style = .plain
rightButton.target = revealViewController()
rightButton.action = #selector(SWRevealViewController.rightRevealToggle(_:))
view.addGestureRecognizer(revealViewController().panGestureRecognizer())
view.addGestureRecognizer(revealViewController().tapGestureRecognizer())
}
This is all the code I use to set up SWRevealViewController in my HomeViewController in order to have left and right menu.
You can now use the delegate method to customize the behavior as it pleases you :
extension HomeViewController: SWRevealViewControllerDelegate {
func revealController(_ revealController: SWRevealViewController!, willMoveTo position: FrontViewPosition) {
view.alpha = position == FrontViewPosition.left ? 1.0 : 0.6
}
}
I have set an alpha on the HomeViewController to make it darker when it's not in front.
For the Storyboard part :
Create an UIViewController in the Storyboard.
Change its class to SWRevealViewController
Create 2 more UIViewControllers (1 for the left menu, 1 for the right menu. If you want only 1 menu, apply for either left or right, as you want)
Link the SWRevealViewController to the 2 UIViewControllers with segues (rear and right segues)
Link the SWRevealViewController to your UINavigationController with the front segue.
Link the UINavigationController with your HomeViewController (with root relationship)
Keep in mind that you may also want to create a custom segue for the transition between your RegisterViewController and the HomeViewController.
You can create it programmatically too:
class RegisterToHomeSegue: UIStoryboardSegue {
override func perform() {
let source = self.source as! RegisterViewController
let homeVC = self.destination as! HomeViewController
let window = source.view.window
window?.rootViewController = homeVC
window?.addSubview(source.view)
UIView.transition(from: source.view, to: homeVC.view, duration: 0.50, options: .transitionCrossDissolve, completion: nil)
}
}
And in the Storyboard change the class segue from your RegisterViewController to the SWRevealViewController to this new one RegisterToHomeSegue and give it an identifier. So that in the RegisterViewController you can call :
performSegue(withIdentifier: "yourIdentifier", sender: nil)
And voilĂ , you now have :
Nice transition between your RegisterViewController and your HomeViewController
Your SWRevealController setup properly, with its left and right menu
Left and Right button item with tap gesture to open the corresponding menu
Change alpha on your HomeViewController when it is open
Tell me if it is helping you.

Resources