Child controller's navigation controller conflicts in UI with parents navigation controller - ios

Steps:
Embedd a navigation controller to entry point controller(ViewController.swift) of main application.
Set entry point to navigation controller.
Add an extra view controller in storyboard and link it to ViewController1.swift file.
Call this function from IB action of ViewController file.
func addChildController(childController : UIViewController) {
let navigation = UINavigationController.init(rootViewController: childController)
navigation.view.frame = self.view.frame
navigation.willMove(toParent: self)
self.addChild(navigation)
navigation.beginAppearanceTransition(true, animated: true)
self.view.addSubview(navigation.view)
navigation.endAppearanceTransition()
navigation.didMove(toParent: self)
}
Result: - Extra space comes between the navigation bar and the below UI, which ties with safe area of view controller.

If you already have your view controller embedded in a Navigation Controller, you should use the same for your child view controllers.
In your case, you have added a Navigation Controller through storyboard, then in func addChildController(childController : UIViewController) { you are adding a new navigation controller again.
I think instead of using this addChildController(), you should use this for pushing next view controller:
extension UIViewController {
func pushVC(vcName : String) {
let vc = UIStoryboard.init(name: "Main", bundle: Bundle.main).instantiateViewController(withIdentifier: vcName)
self.navigationController?.pushViewController(vc, animated: true)
}
}

Related

Missing transition from first view controller to second view controller using navigation controller

I want to create a navigation hierarchy where I want to go to a SecondViewController from FirstViewController using a NavigationController. The FirstViewController contains a button B that I intend to use to go to a SecondViewController. I am employing the navigation controller concept since I want to return to the FirstViewController later. I have done the following steps to achieve it:
I have embedded a NavigationController into a storyboard X containing both the FirstViewController and SecondViewController.
I have created a segue from the button B present in the FirstView (has FirstViewController associated with it) to the SecondView (has SecondViewController).
I set the navigationViewController nvc as following after I have presented the firstViewController:
nvc = UINavigationController.init(rootViewController: firstViewController)
Later on, when the button B gets pressed, I execute the following code to push the secondViewController onto the navigation hierarchy:
self.nvc?.pushViewController(secondViewController, animated: true)
However, the secondView doesn't present itself even after pushing.
I have named the segue between the button B and secondViewController as kSegue, so I tried to perform the segue as an addition to see if the secondViewController presents itself or not:
self.performSegue(withIdentifier: "kSegue", sender: self)
An exception occurs when both the 4th and 5th steps are performed together. The exception states that I'm pushing a viewController that already exists on the navigation hierarchy, but the second view still doesn't open even if I comment the performSegue code.
May I ask what mistake I am making here?
In the storyboard, make sure there is a rootViewController segue from the navigation controller to the first view controller. Also, make sure the navigation controller is marked as the initial view controller.
In the code, change
self.nvc?.pushViewController...
To
self.navigationController?.pushViewController...
1) Take a navigation controller on your storyboard
2) Set navigation controller as initial view controller
3) set view controller A as a root view controller of your navigation controller
4) in "GoToB" method access View controller B's instance and push it in navigation controller
5) On View controller B's "Go Back" method write code to pop it.
6) Dont forget to set storyboard Id on both A & B view controller
class FirstViewController: UIViewController
{
lazy var secondViewController : SecondViewController? =
{
let secondViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController
return secondViewController
}()
override func viewDidLoad()
{
super.viewDidLoad()
}
#IBAction func goToB(sender : UIButton)
{
guard let secondViewController = self.secondViewController else
{
return
}
self.navigationController?.pushViewController(secondViewController, animated: true)
}
}
class SecondViewController: UIViewController
{
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func goBack(sender : UIButton)
{
self.navigationController?.popViewController(animated: true)
}
}

Tabbar not showing when i open from side menu

I am using this code
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "PrintMainViewController")
self.navigationController!.pushViewController(controller, animated: true)
and also added:
self.tabBarController?.tabBar.isHidden = false
If I understand your hierarchy correctly you have a tab bar controller which has navigation controllers in it. So basically any of the tabs can push additional view controllers and tab bar is still visible.
Now you want to push some new controller on the currently selected view controller in the tab bar and you want to do it from another part of the app, another view controller that has no relation to tab bar.
The quickest way to do that is to expose a static instance of your tab bar view controller. This will only work if you always have only 1 tab bar controller in your application (probably 99% of the applications).
First add a current instance to your tab bar view controller:
class MyTabBarViewController: UITabBarController {
static private(set) var currentInstance: MyTabBarViewController?
override func viewDidLoad() {
super.viewDidLoad()
MyTabBarViewController.currentInstance = self
}
}
So when view loads a static value is assigned and can now be accessed anywhere in your project via MyTabBarViewController.currentInstance.
The rest is then just accessing the currently selected view controller and pushing a new view controller. Something like this should do:
let storyboard = UIStoryboard.init(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "PrintMainViewController")
(MyTabBarViewController.currentInstance?.selectedViewController as? UINavigationController)?.pushViewController(controller, animated: true)
You must push in TabBarController
self.tabBarController?.pushViewController(controller, animated: true)

How can I set back to first view controller by navigation controller in code in present without segue?

I have in my project tow view controller and I linked the first VC with navigation controller but the problem is : I used present to go to second VC (that mean I didn't use segue) ... how can I set back to first VC in navigation Controller by code (without segue) .
picture from my storyboard
my code :
let storyboard = self.storyboard
let viewcontroller = storyboard?.instantiateViewController(withIdentifier: "contact_detail") as! ViewController2
viewcontroller.arr2 = arr
present(viewcontroller, animated: true, completion: nil)
With the dismiss method it will work.
Dismisses the view controller that was presented modally by the view controller. (Apple Docs)
self.dismiss(animated: true, completion: nil)
If you are presenting a viewcontroller with present method, you can dismiss it with dismiss method.
If you are adding any view controller with push method then only it will get added to your navigation stack and you can remove it by calling popviewcontroller on it's back action.
Here, you are presenting a viewcontroller, hence it will not get added to your first navigation stack and you can not remove it with pop action on click of back.
If you are looking for a back button like feature on presented view controller, you can add a back button in toolbar, dismiss a viewcontroller on back button action, and can animate it like a popnavigation while dismissing.
extension UINavigationController {
public func removeViewController(classes : [String]) {
var vcs = [UIViewControllers]()
for viewController in self.viewControllers {
let name = viewController.className
if !classes.contains(name) {
vcs.append(viewController)
}
}
if classes.count < vcs.count {
self.viewControllers = vcs
}
}
}
now think you have 4 viewControllers , A, B, C, D, you want to remove B and C and Move Back To A
In D's View Controller
override func viewDidLoad() {
super.viewDidLoad()
//your works
let viewControllersToRemove = [String(describing: type(of:B)), String(describing: type(of:C))]
navigationController.removeViewControoler(classes : viewControllersToRemove)
}
I solved this by using pushViewController
self.navigationController?.pushViewController(MyViewController, animated: true)

Setting back button when presenting a view controller

On clicking a button I display a view controller using the following code:
let navController = UINavigationController(rootViewController: locationVC)
navController.navigationBar.barTintColor = StyleHelper.navBarColor()
navController.navigationBar.tintColor = UIColor.whiteColor()
navController.navigationItem.backBarButtonItem = UIBarButtonItem(image: UIImage(named: "back_arrow"), style: .Plain, target: locationVC, action: nil)
self.presentViewController(navController, animated: true, completion: nil)
But the back button does not appear on the locationVC
What am I doing wrong? Please help
Multiple issues with code.
Issue 1:
You should update viewController's bar button item and not navigationController's bar button item.
So this is wrong
navController.navigationItem.backBarButtonItem
Whats correct
self.navigationItem.backBarButtonItem
Issue 2:
The above code won't work because your current viewController is not pushed by any other VC so it cant show back button. What you need is leftBarButtonItem
So in your VC you can write
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Abcd", style: .done, target: self, action: yourSelectorHere)
}
O/P:
Back button only appears for view controllers in the navigation stack.
you creating new navigation controller stack and making locationVC as root controller so there won't be any view controller to go back.
If you push another view controller to the navigation stack then you will get back button.
In a horizontally regular environment, the view controller is
presented in the style specified by the modalPresentationStyle
property. In a horizontally compact environment, the view controller
is presented full screen by default. doc: presentViewController
So, if you use present a view controller it will not show in navigation controller thus no back button
For this case you need to push view controller.
From storyboard:
let storyBoard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyBoard.instantiateViewController(withIdentifier:
"newViewController") as! YourViewController
self.navigationController?.pushViewController(vc, animated: true)
Programmatically
let vc = YourViewController()
self.navigationController?.pushViewController(vc, animated: true)
presenting will present your VC on top of everything and not push it to the navigayionCOntroller.
You should push your VC to the NavigationController instead like so:
var rootViewController = self.window!.rootViewController as UINavigationController
let mainStoryboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
var profileViewController = mainStoryboard.instantiateViewControllerWithIdentifier("profile") as ProfileViewController
rootViewController.pushToViewController(profileViewController, animated: true)
presentViewController is a method that bring a modal view to your navigation, not part of the navigationController.
Instead you should use the pushToViewController method on the navigationController (and not creating a new one) like that :
if let navigationController = self.navigationController {
navigationController.pushViewController(locationVC, animated: true)
}
The presenting view controller is responsible for dismissing the view controller is presented. If you call this method on the presented view controller itself, UIKit asks the presenting view controller to handle the dismissal.
If you present several view controllers in succession, thus building a stack of presented view controllers, calling this method on a view controller lower in the stack dismisses its immediate child view controller and all view controllers above that child on the stack. When this happens, only the top-most view is dismissed in an animated fashion; any intermediate view controllers are simply removed from the stack. The top-most view is dismissed using its modal transition style, which may differ from the styles used by other view controllers lower in the stack.
If you want to retain a reference to the view controller's presented view controller, get the value in the presentedViewController property before calling this method.
#IBAction func backButtonAction(_ sender: UIButton) {
self.dismiss(animated: true)
}
More: https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismiss

Struggling to push view controller on to UITabBarController

I have an application with 5 tabs(and a UITabBarController). I have more view controllers which can be shown when user clicks buttons etc. Right now I'm trying to push a view controller in the first tab. When I use the following code, the new view controller completely replaces both the tab bar and the previous view controller so the only view controller that is shown is the new one and there's no way to go back. Here's my code:
#objc func openSettingsPage() {
let vc = UIStoryboard(name: "SettingsPage", bundle: nil).instantiateViewController(withIdentifier: "Settings") as! SettingsViewController
self.tabBarController?.show(vc, sender: self)
}
I've also tried:
let vc = UIStoryboard(name: "SettingsPage", bundle: nil).instantiateViewController(withIdentifier: "Settings") as! SettingsViewController
self.navigationController?.pushViewController(vc, animated: true)
Nothing happens when doing that. trying print(self.navigationcontroller) prints "nil" so apparently it doesn't find a navigation controller even though the view controller I'm writing the code in is embedded in a navigation controller

Resources