Swift: Hide status bar on only one child view controller - ios

I have a problem when it comes to the status bar and hiding it.
The setup
I have a BaseViewController that has a slide out menu. This BaseViewController is also the root controller of the application [as set inside AppDelegate]:
window = UIWindow()
window?.makeKeyAndVisible()
window?.rootViewController = BaseController()
As soon as I select a menu item the BaseViewController is populated by the corresponding ViewController [after I embed it into a navigation controller].
Menu item A: ViewControllerA
Menu item B: ViewControllerB
Menu item C: ViewControllerC
Say that I select the Menu Item A (the following code takes place inside BaseViewController):
let activeVC = UINavigationController(rootViewController: ViewControllerA())
view.addSubview(activeVC.view)
addChild(activeVC)
When I select another menu item (say item B), I first remove the previous active view controller (in this case item A) and then I add the ViewControllerB the same way as I did with ViewControllerA:
This is how I remove the previous active view controller:
activeVC.view.removeFromSuperview()
activeVC.removeFromParent()
Manipulating the status bar in each view controller separately:
I set the View controller-based status bar appearance to YES in plist control the appearance of the status bar in every view controller:
Then I go into the ViewController I want to hide the status bar and I add the following code:
override var prefersStatusBarHidden: Bool {
return true
}
The problem
If I want to hide the status bar inside any of the ViewController A, B, or C, I can't. Overriding the prefersStatusBarHidden and setting it to "true" will do nothing.
If I override the prefersStatusBarHidden and setting it to "true" into the BaseViewController, then the BaseViewController and also any of the ViewController A, B, and C will hide the status bar.
What I want
I want to be able to hide the status bar on ViewControllerB without hiding it on the rest. Also a million dollars, but I will settle with the solution!
Thanks in advance!

You'll need to override var childForStatusBarHidden: UIViewController? for BaseController and for UINavigationController. For example:
override var childForStatusBarHidden: UIViewController? {
return children.first
}
and
extension UINavigationController {
open override var childForStatusBarHidden: UIViewController? {
return topViewController
}
}

I had a use case where I had to present a ViewController nested inside a navBar controller the only way which worked for me is adding the following on initilization of a presented ViewController.
modalPresentationCapturesStatusBarAppearance = true

Related

UIView container disappears after adding child view controller

I have a tab bar controller that has two tabs. One is a regular UIViewController and the other is a navigation controller. The navigation controller, I am able to push another view controller on it with a custom inputContainerView with no problems. But when I put a navigationViewController (name from mapbox) on the first tab as a child view, the custom inputContainerView no longer shows up. Even after I remove the child view controller from the first tab.
Adding child to tab 1
...
addChild(navigationViewController)
navigationViewController.view.frame = view.bounds
view.addSubview(navigationViewController.view)
navigationViewController.didMove(toParent: self)
}
func navigationViewControllerDidDismiss(_ navigationViewController: NavigationViewController, byCanceling canceled: Bool) {
navigationViewController.willMove(toParent: nil)
navigationViewController.view.removeFromSuperview()
navigationViewController.removeFromParent()
}
tab 2
override var inputAccessoryView: UIView? {
get { return inputContainerView }
}
override var canBecomeFirstResponder: Bool { return true }
The sequence I am trying to achieve is to add a child view controller to the first tab (navigationViewController which is named this from mapbox) click on the second tab and push a ui view controller on with the inputContainerView showing up. It shows up fine before I add the child view on the first tab but disappears after that
this is what the documentation says:
This method creates a parent-child relationship between the current view controller and the object in the childController parameter. This relationship is necessary when embedding the child view controller’s view into the current view controller’s content. If the new child view controller is already the child of a container view controller, it is removed from that container before being added.
Check the Highlighted part.

Intercepting ios back button to send back to base view controller

We have a sequence of screens that navigate in order...
BaseViewController -> A -> B -> BaseViewController
Each screen uses NavigationController.PushViewController to go from...Base->A, A->B. So each subsequent screen is placed in the navigation stack.
So if you are on A and you click 'back', then it goes back one screen. This works well as it is controlled by the NavigationController.
However, when you are on screen B, 'back' should to back to the BaseViewController.
Instead it goes (as designed by Apple) back to A. Is there a way to intercept the 'back' button on B so we can instead use NavigationController.PopToViewController to send the user back to BaseViewController?
As #the4kman mentioned , we can create a custom button to replace the LeftBarButtonItem ,and handle the back event .
ViewDidLoad in B
this.NavigationItem.LeftBarButtonItem =
new UIBarButtonItem("back", UIBarButtonItemStyle.Plain, (sender,e) => {
UIViewController baseVC = NavigationController.ViewControllers[NavigationController.ViewControllers.Length - 3];
NavigationController.PopToViewController(baseVC, true);
});
As #J.C. Chaparro mentioned , remove A from stack .
ViewDidLoad in B
List<UIViewController> list = NavigationController.ViewControllers.ToList<UIViewController>();
list.RemoveAt(list.Count-2);
NavigationController.ViewControllers = list.ToArray();
You can do something like this in B's viewDidAppear function:
guard let navigationController = self.navigationController else {
return
}
navigationController.viewControllers.remove(at: navigationController.viewControllers.count - 2)
This will remove A from the stack, and allow you to go back to BaseViewController from B.
If I understand correctly you want to pop to root view controller from a certain top view controller. One way to do it would be to create a subclass of UINavigationController and override popViewController method where you would check what you have on top at the moment and decide to pop to root or not. Here's an example:
open class CustomNavigationController: UINavigationController {
override open func popViewController(animated: Bool) -> UIViewController? {
if topViewController is BViewController {
return popToRootViewController(animated: animated)?.last
} else {
return super.popViewController(animated: animated)
}
}
}
Approach:
Use child view controllers
Steps:
Create a view controller C which is a subclass of Base
Add A as a child view controller to C
Create a view controller D which is a subclass of Base
Add B as a child view controller to D
Push C to D
Thanks for all the answers. #Cole Xia put me on the right path for our scenario. His technique works, and the following works as well.
Here is the Xamarin code. The technique is to replace the current list of ViewControllers with a new one. Then when 'back' is hit on B, it goes right back to BaseViewController.
var viewControllers = new UIViewController[] { NavigationController.ViewControllers[0], new B() };
NavigationController.SetViewControllers(viewControllers, false);

Open two controller A & B on one tab item in swift

I have a problem, I want to open two view Controllers on single tab from to different way.
Like:
Login Screen --> Home Screen --> On home screen two button A & B
1 When click on the button A, open A controller on tab controller tab1
2 When click on the button B, open B controller on tab controller tab1
I have 5 tab in tab controller.
Please help me for that issue.
Please refer attached screen for more help.
Thanks,
you can replace ViewController By using this
func tabBarController(tabBarController: UITabBarController, didSelectViewController viewController: UIViewController) -> Bool {
let selectIndex : NSInteger = (tabBarController.viewControllers?.index(of: viewController))!
if (selectIndex == 1) {
let vc = UIViewController() // your new Controller
var allviews = tabBarController.viewControllers
allviews?.remove(at: selectIndex)
allviews?.insert(vc, at: selectIndex)
tabBarController.setViewControllers(allviews, animated: true)
return false;
}
return true;
}
(Please add some more information to your question, or at least code to know what have you tried.)
By your question, It seems you need a Tab Bar Controller.
You use tab bar controller to organize your app into one or more distinct modes of operation. The view hierarchy of a tab bar controller is self contained. It is composed of views that the tab bar controller manages directly and views that are managed by content view controllers you provide. Each content view controller manages a distinct view hierarchy, and the tab bar controller coordinates the navigation between the view hierarchies.

Swift: How to segue between view controllers and use navigation bar to go backwards within a child view controller (XLPagerTabStrip)

I am currently implementing the XLPagerTabStrip (https://github.com/xmartlabs/XLPagerTabStrip) which effectively creates a tab bar at the top of the view controller. I want to be able to segue to a new view controller from one of the tabbed controllers and be able to use the navigation bar to move backwards (or a custom version of the navigation bar if this isn't possible).
XLPagerTabStrip provides the moveToViewController and moveToViewControllerAtIndex functions to navigate between child view controllers, but this method doesn't allow use of a navigation bar to go backwards.
Conceptually XLPagerTabStrip is a collection of view controllers declared and initialized during the XLPagerTabStrip model creation.
It has virtually no sense to use a UINavigationController if you already have all the viewcontrollers available.
You can create a global var previousIndex to store the previous viewController index and allow users to go back by using canonical methods:
func moveToViewControllerAtIndex(index: Int)
func moveToViewControllerAtIndex(index: Int, animated: Bool)
func moveToViewController(viewController: UIViewController)
func moveToViewController(viewController: UIViewController, animated: Bool)
About a new viewController, suppose you have 4 viewControllers that built your container (XLPagerTabStrip) named for example z1, z2, z3 e z4.
You can embed to z4 a UINavigationController (so it have the z4 controller as rootViewController) and start to push or pop your external views. When you want to return to your z4 you can do popToRootViewControllerAnimated to your UINavigationController
When you are go back to z4 , here you can handle your global var previousIndex to moving inside XLPagerTabStrip.
I'm not familiar with XLPagerTabStrip, but I had a similar problem recently and the solution was to use an unwind segue to go back to the previous view controller. It's pretty trivial to implement so probably worth a try.
To navigate back to your previous view tab controller, you had initially navigated from;
Embed your new view controller, from which you wish to navigate
away from in a navigation bar
Connect it's Navigation Bar Button to the Parent view containing the
tab bar by dragging a segue between the 2 views
Create a global variable in App delegate to store current index
which you will use in the Parent view to determine what tab view
controller to be shown
var previousIndex: Int = 0 //0 being a random tab index I have chosen
In your new view controller's (the one you wish to segue from)
viewdidload function, create an instance of your global variable as
shown below and assign a value to represent a representative index
of the child tab bar view controller which houses it.
//Global variable instance to set tab index on segue
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.previousIndex = 2
You can write this for as many child-tab connected views as you wish, remembering to set the appropriate child-tab index you wish to segue back to
Now, create a class property to reference your global variable and a function in your Parent view as shown below
let appDelegatefetch = UIApplication.shared.delegate as! AppDelegate
The function
func moveToViewControllerAtIndex(){
if (appDelegatefetch.previousIndex == 1){
self.moveToViewControllerAtIndex((self.appDelegatefetch.previousIndex), animated: false)
} else if (appDelegatefetch.previousIndex == 2){
self.moveToViewControllerAtIndex((self.appDelegatefetch.previousIndex), animated: false)
}
}
You may now call this function in the Parent View Controller's viewDidLoad, as shown below.
moveToViewControllerAtIndex()
Run your project and that's it.

UITabBarController - How to switch to tab of a particular type

I have a UITabBarController implemented by a custom class (eg HomeTabBarController) and in my storyboard I've attached to it 3 ViewController as it's child.
I know that I can use, in order to select a particular view controller, in my UITabBarController:
selectedIndex = 2
But I would like to make my project a bit more flexible, so I would like to select a child tab only knowing it's type, not it's position. How can I do it?
As described in the StackOverflow documentation I'm going to answer my own question.
Let's make an example, you have a UITabBarController with 3 childs:
HomeTabBarController
CustomAViewController
CustomBViewController
CustomCViewController
In your HomeTabBarController controller you can put a func as this:
func selectCustomATab() {
var tab = 0
for v in viewControllers! {
for k in v.childViewControllers {
if k is CustomAViewController {
tab = viewControllers!.indexOf(v)!
}
}
}
selectedIndex = tab
}
And that's all, you can repeat for every ViewController child as you like.
Then in any ViewController child you can do something like that in order to switch tab:
(self.tabBarController as! HomeTabBarController).selectCustomATab()
Completely ignoring what's the CustomAViewController position in the tab array.
set you're tab bar index value
tabBarController?.selectedIndex = 2

Resources