How to hide a bar button item for certain users - ios

I have a settings bar button item (set as left bar button item). I only want to display it if the user is logged in.
I thought I could use the following for anonymous users
navigationItem.leftBarButtonItem = nil
But then how would I show it as soon as they logged in?

You can store a copy of the leftBarButtonItem in a strong property and update it after the users log in.
var leftBarButtonItem : UIBarButtonItem!
Inside viewDidLoad:
self.leftBarButtonItem = UIBarButtonItem(title: "test", style: UIBarButtonItem.Style.Plain, target: nil, action: nil)
In logic:
if loggedIn
{
self.navigationItem.leftBarButtonItem = self.leftBarButtonItem
}
else
{
self.navigationItem.leftBarButtonItem = nil
}

Best Way is just custom your Bar buttom with image. Set barbuttom.image = nil to Hide again assign the image to show. And dont forget to make the barbutton isEnabled as false.

I have more that 2 menuitems and remove/add menuitem is an overhead. This code snippet worked for me.
func showMenuItem(){
menuItemQuit.customView?.isHidden = false
menuItemQuit.plainView.isHidden = false
}
func hideMenuItem(){
menuItemQuit.customView?.isHidden = true
menuItemQuit.plainView.isHidden = true
}

if you want to hide/show UIBarButtonItem : For Swift 3
Used below simple code :
Declaration :
var doneButton = UIBarButtonItem()
In ViewDidLoad() or ViewWillAppear() or where you want to hide it : [hide bar button]
self.navigationItem.rightBarButtonItem = nil
where you want to show bar button : [use anywhere in your code]
self.navigationItem.rightBarButtonItem = self.doneButton
doneButton = UIBarButtonItem(title: "Done", style: UIBarButtonItemStyle.plain, target: self, action: #selector(YourViewController.dismissPicker))

Swift 5.x
I faced the same dilemma and unfortunately no solution worked for me. Adding and removing buttons and related segues is unnecessarily too much of code when it includes multiple buttons on multiple screens. I have taken this approach for one or two buttons in the past and it becomes pretty ugly pretty fast.
The code menuItemQuit.customView?.isHidden = false doesn't seem to work on iOS 13 and above either, otherwise it would have made life so much easier.
My approach was to simply disable the bar button and change its tint to the navigation colour's tint.
In my app What.To.Eat I display bar buttons based on user's login status. Every element of the app is themed so that I could control all the colors based on various factors.
The navigation bar's color is named commonButtonColor and the bar buttons tint color is named commonButtonColor.
When I have to hide a bar button, I simply do the following:
let nav = self.navigationController?.navigationBar
nav?.tintColor = Theme.shared.titleText
nav?.barTintColor = Theme.shared.headerBg
if person.loggedIn {
mealPrefsBarButton.tintColor = Theme.shared.commonButtonColor
mealPrefsButton.isEnabled = true
} else {
mealPrefsBarButton.tintColor = Theme.shared.headerBg
mealPrefsButton.isEnabled = false
}
Where theme colors are defined in a separate file like this:
static var headerBg: UIColor {
return UIColor(red: 0.965, green: 0.969, blue: 0.973, alpha: 1.00)
}
The above is a simplified version of what I do in the app to make it clear what I am doing. I hope it would help someone trying to achieve the same. It is simple solution and works just perfectly with a few lines of code.
As an example from the app, this is how two buttons appear and disappear based on whether the My Recipes button is selected or not:

I have a same problem and solved. I have a bar button item with image
barbtnClose.isEnabled = false
barbtnClose.image = nil
barbtnClose.customView?.isHidden = true // do not work in iOS 13

Swift 5
A better solution and works even if you have set a custom navigation bar.
Hide navigation bar button item or back button leftBarButtonItem / rightBarButtonItem
if login == true {
self.navigationItem.leftBarButtonItem = nil
} else {
print("set your bar button or return")
}
Hide back bar button in navigation controller with swift 5
self.navigationItem.leftBarButtonItem = nil
self.navigationItem.hidesBackButton = true

Related

How to disable backBarButtonItem? [duplicate]

Is there any official way how to set UIBarButtonItem.enabled property? I tried to set a backButtonItem in previous controller. But enabled property is ignored.
More in this simple example project.
I don't want to some solution like "make your own leftBarButtonItem and set its alpha ..."
Edit: I don't want to hide it, only disable it with dimmed colour and disabled user interaction. It's exactly the same behaviour as for disabled leftBarButtonItem.
As of today it is not possible to disable the back button using the enabled property. The backBarButtonItem property will be nil unless you create a custom item and even then it will ignore the enabled property. There are a couple (non-satisfactory) ways around this.
Hide the button
This is what Apple wants you to do given that they ignore the enabled property. It is as simple as
navigationItem.hidesBackButton = true
and should be the preferred approach unless you have good reasons.
Disable and Tint the Navigation Bar
You can disable user interaction on the whole navigation bar and tint it to make the back button appear disabled.
navigationController?.navigationBar.isUserInteractionEnabled = false
navigationController?.navigationBar.tintColor = UIColor.lightGray
This does, unfortunately, affect other elements in the navigation bar as well so it might not be an option if, for instance, you have another bar button item on the right side.
Use a Custom Left Bar Button Item
The leftBarButtonItem does not ignore the enabled property so you could create a custom item and trigger the pop manually when it is activated.
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(ThisClass.backButtonTapped))
...
navigationItem.leftBarButtonItem?.isEnabled = false
func backButtonTapped() {
self.navigationController?.popViewController(animated: true)
}
This will, however, not have the back bar button style with the leading triangular indicator.
Add below code in your ViewController2.swift Class.
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.hidesBackButton = true;
}
It will hide your back button.
If you want to hide it, UInavigationItem has a hidesBackButton property.
I know this is an old thread, but this may help someone else.
As mentioned by hennes, you can no longer disable the back button. Instead, you will need to disable the entire navigationBar.
The approach I took, was disabling the navigationBar, and then applying an 0.5 alpha to the subviews of the navigation bar.
In your ViewController class:
func changeBarButtons(alpha: CGFloat) {
navigationController?.navigationBar.subviews.forEach { firstViews in
firstViews.subviews.forEach { view in
if ["_UIButtonBarButton", "_UIButtonBarStackView"].contains(type(of: view).description()) {
view.alpha = alpha
}
}
}
}
func set(loading: Bool) {
let alpha: CGFloat = loading ? 0.5 : 1
navigationController?.navigationBar.isUserInteractionEnabled = !loading
changeBarButtons(alpha: alpha)
}
Keep in mind, that Apple could change the names of the class any time. That being said, it's highly unlikely they do so. If you don't mind the title of the View Controller fading out, you can apply the alpha to all the subviews, without checking the class name.
Don't try to disable your custom back button (won't work), just set a new one which is disabled. You can reach the previous navigation item through the UINavigationBar.backItem property.
// set disabled back button
let backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItem.Style.plain, target: nil, action: nil)
backButton.isEnabled = false
navigationController?.navigationBar.backItem?.backBarButtonItem = backButton
// disable pop gesture
navigationController?.interactivePopGestureRecognizer?.isEnabled = false

How to change Back button text from within the child view controller?

I know that you can set the title of the back button from the IB or in prepareForSegue, but in my app I need to update the title according to events that can happen while the controller is visible.
I tried but nothing happens:
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: title, style: .Plain, target: nil, action: nil)
This works though but has no back arrow:
self.navigationItem.leftBarButtonItem = UIBarButtonItem(title: title, style: .Plain, target: nil, action: "popVC")
Any ideas?
The backBarButtonItem is the item used for the back button of the next controller in the navigation stack.
So for example if you have a navigation controller with a root viewController A and you push a viewController B, then the back button title that you see once B is pushed is configured using A.
You could have something like this :
A.navigationItem.backBarButtonItem = UIBarButtonItem(title: "Go back to A", style: .Plain, target: nil, action: nil)
Once B is pushed, you see a back button with "Go back to A".
In your case the tricky part is to find A in the navigation stack from B.
You can do it by searching in the viewControllers of the navigationController like so :
// This code works from the `B` view controller
let viewControllers = self.navigationController?.viewControllers ?? []
if let indexOfCurrent = viewControllers.indexOf(self) where (indexOfCurrent > viewControllers.startIndex) {
let indexOfPrevious = indexOfCurrent.advancedBy(-1)
let previousViewController = viewControllers[indexOfPrevious]
previousViewController.navigationItem.backBarButtonItem?.title = "New Title"
}
Edit
I don't know any clean way to refresh the navigation bar after that. Maybe you could ask a separate question just for that.
What you could do is pop and push the view controller without animation
if let navigationController = self.navigationController {
navigationController.popViewControllerAnimated(false)
navigationController.pushViewController(self, animated: false)
}
Or maybe try to create a new UIBarButtonItem instead of changing the title of the existing one.
try this:
let baritems = self.navigationController?.navigationBar.items
for item in baritems!{
if (item.leftBarButtonItem != nil){
item.title = "123"
}
}
This question is pretty old now, but I was a problem changing the back button text based on a nested tableViewController's selected cell.
It looks like the default back button for a navigationViewController bases its text on the title of the view controller it takes the user back to.
You should probably be careful, as my solution makes a couple assumptions that I'm not positive will always be true.
let vcs = navigationController?.viewControllers
vcs?[(vcs?.count)! - 2].navigationItem.title = "Your Text Here"
Assuming that our current view controller is at the top of the navigation stack -> vcs[vcs.count - 1]; there exists a view controller on that stack before this one -> vcs[vcs.count - 2]; and the navigationItem.title of that preceding view controller can be changed without unwanted side effects; we can change our back button text by changing the navigation title of that preceding view controller.

Swift - navigation buttons show & hide

Running into a really weird issue and couldn't find how to fix it. I have tab bar controller based app with 3 view controllers (2 table view controllers and one regular vc), they are Profile, Orders, People. When I sign in, I land on profile and the buttons in the nav bar show properly. As soon as I go to People and come back, the buttons disappear and don't show anymore. Also vice versa is true (going from People to Profile).
This is the additional weird part: if I go from profile to orders, then back, it will show all buttons. Also if I go from profile to orders to people, people shows normally.
Here is the structure:
In all viewDidLoad and viewWillAppear, I add the following code:
let editButton = UIButton()
editButton.frame = CGRectMake(0, 0, 60, 35)
editButton.setTitle("Edit Profile ", forState: .Normal)
editButton.setTitleColor(UIColor.blueColor(), forState: .Normal)
editButton.addTarget(self, action: Selector("editButtonPressed"), forControlEvents: .TouchUpInside)
let leftBarButton = UIBarButtonItem()
leftBarButton.customView = editButton
self.tabBarController?.navigationItem.leftBarButtonItem = leftBarButton
and in the viewWillDisappear, I add:
self.tabBarController?.navigationItem.leftBarButtonItem = nil
self.tabBarController?.navigationItem.rightBarButtonItem = nil
All except Orders table view controller doesn't have any of the above code. What is going on? I will be happy to provide more if needed, but this is really all there is to it as far as I understand.
You can try this:
1.Show:
self.navigationItem.rightBarButtonItem?.customView?.alpha = 1.0
2.hide:
self.navigationItem.rightBarButtonItem?.customView?.alpha = 0.0

Need help hiding/showing custom navBar IOS Swift

I need help showing and hiding my custom navigation bar that is not linked with a navigation controller. I have initialized my nav bar and UITapGestureRecognizer in viewWillAppear and want to have the gesture selector to show and hide the nav bar. I have tried numerous navigationController functions but since I have not embedded one they do not seem to work:
navigationController?.barHideOnTapGestureRecognizer
// OR
navigationController?.hidesBarsOnTap = true
// OR
navigationController?.setNavigationBarHidden(true, animated: true)
I have seen that the:
navigationBar.hidden = true //OR FALSE
can change the initial appearance of the navBar, but it cannot toggle show/hide like i need it to. Basically it just needs to mimic the apple Photos app. Thanks in advance.
One way to go is to create an IBAction for your custom navigation bar. In this IBAction you set
navigationBar.hidden = true
or
navigationBar.hidden = false
depending on the current hidden state
Or if you create your elements programmatically, you can try an approach using Target and Event for your UIGestureRecognizer.
First you add a Target to your element:
gestureRecognizer.addTarget(self, action: "pressed:", forControlEvents: .TouchUpInside)
In action parameter of .addTarget, you set the method name that should be called.
Then write the method where you alter the hidden state:
func pressed(sender: UIGestureRecognizer!) {
if navigationBar.hidden = true {
navigationBar.hidden = false}
else{
navigationBar.hidden = true}
}

How to disable back button in navigation bar

Is there any official way how to set UIBarButtonItem.enabled property? I tried to set a backButtonItem in previous controller. But enabled property is ignored.
More in this simple example project.
I don't want to some solution like "make your own leftBarButtonItem and set its alpha ..."
Edit: I don't want to hide it, only disable it with dimmed colour and disabled user interaction. It's exactly the same behaviour as for disabled leftBarButtonItem.
As of today it is not possible to disable the back button using the enabled property. The backBarButtonItem property will be nil unless you create a custom item and even then it will ignore the enabled property. There are a couple (non-satisfactory) ways around this.
Hide the button
This is what Apple wants you to do given that they ignore the enabled property. It is as simple as
navigationItem.hidesBackButton = true
and should be the preferred approach unless you have good reasons.
Disable and Tint the Navigation Bar
You can disable user interaction on the whole navigation bar and tint it to make the back button appear disabled.
navigationController?.navigationBar.isUserInteractionEnabled = false
navigationController?.navigationBar.tintColor = UIColor.lightGray
This does, unfortunately, affect other elements in the navigation bar as well so it might not be an option if, for instance, you have another bar button item on the right side.
Use a Custom Left Bar Button Item
The leftBarButtonItem does not ignore the enabled property so you could create a custom item and trigger the pop manually when it is activated.
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Cancel", style: .plain, target: self, action: #selector(ThisClass.backButtonTapped))
...
navigationItem.leftBarButtonItem?.isEnabled = false
func backButtonTapped() {
self.navigationController?.popViewController(animated: true)
}
This will, however, not have the back bar button style with the leading triangular indicator.
Add below code in your ViewController2.swift Class.
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.hidesBackButton = true;
}
It will hide your back button.
If you want to hide it, UInavigationItem has a hidesBackButton property.
I know this is an old thread, but this may help someone else.
As mentioned by hennes, you can no longer disable the back button. Instead, you will need to disable the entire navigationBar.
The approach I took, was disabling the navigationBar, and then applying an 0.5 alpha to the subviews of the navigation bar.
In your ViewController class:
func changeBarButtons(alpha: CGFloat) {
navigationController?.navigationBar.subviews.forEach { firstViews in
firstViews.subviews.forEach { view in
if ["_UIButtonBarButton", "_UIButtonBarStackView"].contains(type(of: view).description()) {
view.alpha = alpha
}
}
}
}
func set(loading: Bool) {
let alpha: CGFloat = loading ? 0.5 : 1
navigationController?.navigationBar.isUserInteractionEnabled = !loading
changeBarButtons(alpha: alpha)
}
Keep in mind, that Apple could change the names of the class any time. That being said, it's highly unlikely they do so. If you don't mind the title of the View Controller fading out, you can apply the alpha to all the subviews, without checking the class name.
Don't try to disable your custom back button (won't work), just set a new one which is disabled. You can reach the previous navigation item through the UINavigationBar.backItem property.
// set disabled back button
let backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItem.Style.plain, target: nil, action: nil)
backButton.isEnabled = false
navigationController?.navigationBar.backItem?.backBarButtonItem = backButton
// disable pop gesture
navigationController?.interactivePopGestureRecognizer?.isEnabled = false

Resources