Need help hiding/showing custom navBar IOS Swift - ios

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}
}

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

UISearchController Needs an extra tap to become first responder

I switched my apps one screen from UISearchBar to UISearchController. It's a tableview controller. As per design I should not keep the search bar on UI initially unless it is activated, (Normally it's a common practice to keep search bar as the 'tableHeaderView'). The problem was, I have a search button, when tapped 'search bar' should be activated and become first responder.
When tapped on cancel button, it should be removed from UI. However when I'm tapping on the 'Search Bar Button' on navigation bar, the UISearchController gets activated, providing a dim background but the keyboard doesn't appear. I need to tap one more time on search bar to bring the keyboard upon UI.
Here's my search bar button action:
#IBAction func onTapSearch(_ sender: AnyObject) {
self.view.addSubview(searchController.searchBar)
searchController.isActive = true
searchController.becomeFirstResponder()
isSearchActive = true
self.navigationController?.setToolbarHidden(true, animated: false)
}
I'm configuring the UISearchController in my viewDidLoad method. Let me know if that part code is any of you want to see, however it's usual code. And I verified I'm not calling anywhere resignFirstResponder() method anywhere.
try this,
Just replace this line,
searchController.becomeFirstResponder()
With this below,
searchController.searchBar.becomeFirstResponder()
Edit,
func didPresentSearchController(_ searchController1: UISearchController) {
searchController1.searchBar.becomeFirstResponder()
}
Implement this delegate method and try.

How to change UIBarButtonItem text during runtime "cleanly" (Edit --> Done --> Edit)?

I'm trying to change UIBarButtonItem text "cleanly" during runtime so that Edit/Done modes can be toggled. Every time I change the title attribute during runtime, however, the animation seems clumsy. I'm looking to emulate the appearance and function of the Edit/Done button in the Contacts app (where Edit simply fades and Done appears in its place).
#IBAction func editDoneButtonPressed(sender: UIBarButtonItem) {
if(sender.title == "Edit"){
sender.title = "Done"
}else{
sender.title = "Edit"
}
}
Initial settings: Identifier is set to "custom" and Title is set to "Edit"
Simpler programatical solutions are preferred, however, the appearance of the animation is indeed paramount. I'd consider toggling the UIBarButtonItem identifier, rather than its text attribute, however, I'm not sure of an elegant way to toggle the identifier during runtime.
BTW: I'm using Swift to construct the app.
Links to...
Screencast of Contacts app Edit/Done toggle animation:
https://youtu.be/5mT_vzpNhIw
Screencast of my Edit/Done toggle animation:
https://youtu.be/mEweotJNVNE
Thank you.
There's a much better way, a standard way, to get an Edit/Done button.
UIViewController provides a standard Edit/Done button that is already hooked into the editing property of the view controller.
A common usage is as follows:
Inside the viewDidLoad method of your view controller, use the standard button:
self.navigationItem.rightBarButtonItem = editButtonItem
Put the editButtonItem wherever you actually need it.
Then, instead of setting up your own action, simply override the setEditing(_:animated:) method:
override func setEditing(_ editing: Bool, animated: Bool) {
super.setEditing(editing, animated: animated)
if (editing) {
// user just tapped the Edit button (it now says Done)
} else {
// user just tapped the Done button (it now says Edit)
}
}

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

How to hide a bar button item for certain users

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

Resources