I have a View Controller which is the 2nd View Controller and need a navigation bar. The 1st one is a controller which doesn't need a Nav Bar and 3rd View Controller need a navigation bar.
As per stacks 3rd view controller will be no top of stack.
I have implemented these methods in 2nd View Controller:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if self.navigationController?.navigationBarHidden == true{
self.navigationController?.navigationBarHidden = false
}
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .Plain, target: nil, action: nil)
self.navigationController?.navigationBar.tintColor = Quikr_Util.colorWithHexString("#0083cb")
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
if self.navigationController?.navigationBarHidden == false{
self.navigationController?.navigationBarHidden = true
}
}
The things were working fine if a use a back bar button to go back from 3rd to 2nd View Controller.
Things got messy when I started to slide from one View Controller to another for example, when I slide half and then release.
It means sliding from 3rd to 2nd but release in between so it goes to 3rd View Controller rather than second .
What may be the best way to hide and unhide view navigation bars .
Secondly, how do sliding works , which functions will get called when sliding happens?
For your first question
To hide and show the Navigation Controller, in 2nd and 3rd View Controllers, You can use:
Hide:
navigationController?.setNavigationBarHidden(true, animated: false)
Show:
navigationController?.setNavigationBarHidden(false, animated: false)
There is not really a need to check if it is hidden or not.
For the second question, you can use UIGestureRecognizer
Edit:
Just to be clear, instead of hiding and showing the in the same file, hide and show in viewDidAppear() in needed swift files
Related
I have two viewcontrollers. The first viewcontroller is collection view controller and i set self.navigationController?.hidesBarsOnSwipe = true in viewDidLoad().
When I push the second viewController from the visible cell of collectionView, the navigation bar is showing in the second viewController but if I scroll the collectionView cell and when push the navigation is not showing.
Can anyone tell me what the problem is?
scrolling is done via swipe gesture, so it triggers your code:
self.navigationController?.hidesBarsOnSwipe = true
because navigation controller is shared between all view controllers presented on top of it, it's properties (like hidden bar) preserves pushing / popping.
Common pattern is to change it's state in overrided lifecycle methods, eg:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.hidesBarsOnSwipe = false
self.navigationController?.setNavigationBarHidden(false, animated: true)
}
and reverting those state in viewWillDisappear
When this property is set to true, an upward swipe hides the navigation bar and toolbar. A downward swipe shows both bars again. If the toolbar does not have any items, it remains visible even after a swipe. The default value of this property is false. (get it from apple)
See the doc https://developer.apple.com/documentation/uikit/uinavigationcontroller/1621883-hidesbarsonswipe
It means when you swipe up it will hide and when swipe down it will shown. That's the reason.
To fix it you can add following code to the other controller
[self.navigationController setNavigationBarHidden:NO animated:YES];
Don't get much insight what exactly you implemented, but try to unhide navigation bar in second view controller.
Add below code in viewDidLoad method of second View controller.
self.navigationController?.isNavigationBarHidden = false
Put this self.navigationController?.hidesBarsOnSwipe = false and this self.navigationController?.setNavigationBarHidden(false, animated: true) in your second view controller.
You might want to move your self.navigationController?.hidesBarsOnSwipe = true from viewDidLoad to viewWillAppear in your first view controller.
If I have a UITabBarController and do this inside one of its view controllers:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
tabBarController?.tabBar.isUserInteractionEnabled = false
}
the first time it shows, I can still change tabs.
If I tap on another tab and tap back on the first tab, then the tabbar is really disabled, but not the first time.
Why? And how do I solve this?
EDIT:
There is one detail I noticed, the tabBarController?.tabBar.isUserInteractionEnabled = false has to be on the second view controller of a navigation controller. In other words:
Say I have that structure
UITabBarController
UINavigationController
UIViewController (1)
UIViewController (2)
UIViewController (3)
So if I add that viewDidAppear code on view controller (2), you can change the tab once, but not the second time (after you navigate to it, obviously).
And there is more, if I go back after navigating to view controller (2), the tab bar becomes "interactable" again, without my setting it to true.
Having a tab bar in view, but not being able to interact with it will probably be confusing and frustrating for the user. And while I don't have the reason or solution for the original question, I have an alternative suggestion:
Hide the tab bar in UIViewController (2):
override func viewDidLoad() {
super.viewDidLoad()
tabBarController?.tabBar.isHidden = true
}
We're putting this in viewDidLoad so it's hidden as soon as the view
appears.
This also requires that you explicitly unhide it in UIViewController (1) for when the user hits the back button. Do it in viewWillAppear since the view was loaded already and we're going back to it.
override func viewWillAppear(_ animated: Bool) {
tabBarController?.tabBar.isHidden = false
}
While I have experienced the same behavior with:
tabBarController?.tabBar.isUserInteractionEnabled = false
..what you CAN do is disable each of the items in the tabBar.items collection, and then re-enable them in the viewWillAppear method of another controller. For example, if you didn't want your users tabbing out of menu #1 once they are inside it, you could do something like this in the subsequent controller(s):
override func viewWillAppear(_ animated: Bool) {
//0th tab items remains enabled as "only choice" for user
self.tabBarController!.tabBar.items![1].isEnabled = false
self.tabBarController!.tabBar.items![2].isEnabled = false
self.tabBarController!.tabBar.items![3].isEnabled = false
self.tabBarController!.tabBar.items![4].isEnabled = false
}
when the user tabs back to first tab (0) (or hits the back button), in that viewController's viewWillAppear method, re-enable the items:
override func viewWillAppear(_ animated: Bool) {
//re-enable tab items
self.tabBarController!.tabBar.items![1].isEnabled = true
self.tabBarController!.tabBar.items![2].isEnabled = true
self.tabBarController!.tabBar.items![3].isEnabled = true
self.tabBarController!.tabBar.items![4].isEnabled = true
}
Consider a storyboard where we have UITabBarController, in it any UIViewController(lets call it VC) embedded in a UINavigationController. We want VC to have a BarButtonItems on its navigation bar. This storyboard is presented by push segue from another storyboard (having another navigation controller).
Everything looks OK in XCode, but navigation bar does not change in VC at the runtime. However when I change presenting this storyboard from push to modal, everything seems to be fine. IMHO it is because of embedding the navigation controller but I do not see any reason why it is not working. Any idea how to fix it legally (presenting by push) and without any pain would be helpful.
Thanks in advance
So I think you will have to employ some code to fix your issue but not much. I built a test project to test this and will attach images along with code.
First if I understand you correctly you have a navigationController push the new storyboard in question. See attached image.
I named the storyboard being pushed because that is what is happening. Then in my storyboard named Push here is the setup.
In the first view controller of the tabbarcontroller I added the below code. Obviously this hides the navigation controller that pushed us here. If you then visit controller number 2 our new navigation controller and items show. If hiding the navigation controller in the tabbarcontroller view controller 1 is not what you want to do then. continue reading.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
//or to unhide from returning the opposite ->self.parent?.navigationController?.isNavigationBarHidden = true
self.parent?.navigationController?.isNavigationBarHidden = true
}
If you did not want to hide the navigation controller in the first view controller but when visiting controller 2 you want to see your items then add this to your viewWillAppear and in the first controller in viewWillAppear change the code from true to false.
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Do any additional setup after loading the view, typically from a nib.
self.parent?.navigationController?.isNavigationBarHidden = true
}
This hides the parent navigation controller as basically that was covering up your navigation controller in your example. So above hides the parent navigation controller. This is also why presenting modally worked. Your navigation controller was hidden from the start. Hope this helps.
**Edit
If you want the navigation controller in tab 2 view controller but you want to keep the parent in tab one to be able to go back with the back button you can set this in viewWillAppear instead so it would look like this in view controller 1.
//tabcontroller vc 1
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.isNavigationBarHidden = false
}
And in tabcontroller view controller 2 with the item in the bar you could do this.
//tabbarcontroller vc 2 with own navigationcontroller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.parent?.navigationController?.isNavigationBarHidden = true
}
Finally if you want the back button visible in both controllers but want different right buttons do it programmatically in viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.navigationItem.setRightBarButton(UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(FirstViewController.editSomthing)), animated: true)
}
And if you want to remove it in the other controller
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.navigationItem.rightBarButtonItem = nil;
}
In Both of the above examples directly above this, we are keeping the parent navigation controller so you would not need to embed your view controllers of the tab controller inside uinavigation controller.
You could also use a combo of the above code if you want the hide/show parent navigation controller in viewWillAppear as well. Some of this is dependent on the view hierarchy you choose now and in the future.
I have read about similar problems where the problem was caused by having multiple navigation controllers, but I only have one. This is my navigation flow.
VC = UIViewController, NC = UINavigationController
VC1 -modal-> NC -root-> VC2 -show-> VC3
VC1 is not embedded in a navigation controller, I'm starting that modal segue using performSegueWithIdentifier:sender:.
VC2 then is using a show segue to present VC3, which is the one where the back button is not visible. It still works though. But, it does appear if I exit to the home screen and then enters the app again, as shown here:
https://gfycat.com/VelvetyThisHamster.
Any ideas why this is happening?
edit: To make things clear: I want the button both visible and functioning (it's not that it's working that's the problem, but that it's hidden)
EDIT 2:
If I change my navigation flow to this
NC -root-> VC2 -show-> VC3
then the back button works as intended. So the question is, how can I add a regular view controller without a navigation controller before the first navigation controller? I want it before because VC1 should not have a navigation bar and VC2 should be presented modally.
try this
Hidden
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
//use this
self.navigationItem.setHidesBackButton(true, animated: false)
//else use this
self.navigationItem.leftBarButtonItem = nil
}
Show
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
//use this
self.navigationItem.setHidesBackButton(false, animated: false)
//else
self.navigationController.navigationItem.backBarButtonItem.enabled = TRUE
}
Update
override func viewWillAppear(animated: Bool)
{
super.viewWillAppear(animated)
//use this
self.navigationItem.setHidesBackButton(false, animated: false)
//else
let backButton = UIBarButtonItem(title: "leftbutton", style: UIBarButtonItemStyle.Plain, target: self, action: "buttonMethod")
self.navigationItem.leftBarButtonItem = backButton
}
func buttonMethod() {
print("Perform action")
}
I think I found the source of the problem, so I'll post it here in case someone else runs into the same problem.
The modal presentation between VC1 and NC was made from a background queue (by calling performSegueWithIdentifier:sender: in the completion handler of an NSURLSessionDataTask to be precise). By dispatching that line of code to the main queue the problem seems to disappear.
Turn out, I had NavigationBar tint color set to "Clear". Once I changed it, the back button appeared.
I have a main page, which has a button that will go to another page.
I want when i go the second page, to have a back button at the top left of the navigation bar.
I added a navigation controller to the main view controller, and then i added a push segue to my second view controller (second page), so an automatic back button created.
what i want is that i do NOT want the navigation bar to be in the main view controller, and I don't want it to be in the second view controller, I just want to have the back button in the second view controller.
I thought about it and i came up doing:
self.navigationController?.setNavigationBarHidden(true, animated: false)
in the main view controller, that actually hide the navigation, but that makes the second view ctonroller to loose its back button.
do you have any solution to my problem?
this is the main view controller (which has the navigation bar, but i would like to not have it)
this is the second view controller, which has the back button,
I have no problem if i leave the navigatino bar, if it was transparent, any idea please? (and by transparent, i mean i can see my image bellow it)
Update 1
after the first comment gives me a hint, i tried to applied it like this:
class CustomNavigationBar: UINavigationBar {
override func drawRect(rect: CGRect) {
super.drawRect(rect)
}
}
and I set the class of my navigation bar in the UINavigationControlelr to my custom navigation bar.
and in my main view controlelr i add this:
self.navigationController?.navigationBar.translucent = true;
self.navigationController?.navigationBar.backgroundColor = UIColor.clearColor()
but the result is that my Main view controller, still has a place (though it is empty) for the navigation bar. can't i make this place as transparent to see the image bellow it?
add this to the main view controller
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
self.navigationController?.navigationBar.shadowImage = UIImage()
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: true)
}
override func viewWillDisappear(animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(false, animated: true)
}