Setting navigationItem title delay issues - ios

When pushing a controller to the navigation stack i execute:
self.navigationItem.title = "";
As i dont want the next view to show the name of the previous controller on the back button.
When i get back to that controller i do this:
override func viewWillAppear(_ animated: Bool) {
self.navigationItem.title = "Title Of View";
...
}
The title is set appropriately but it lags for about 1 second or so.. I remember using this techinique for quite some time without having a problem in the past. The code that sets the titles isn't within a network call or anything like that..
Any ideas about what might be causing this?

In order to hide the back button title you should set empty UIBarButtonItem instance to the self.navigationItem.backBarButtonItem
right after you push the new view controller, like so:
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .done, target: nil, action: nil)
This way you don't have to manipulate the title of the view controller.

Related

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.

implement a simple back button swift ios

I have a navigation bar placed directly on my scene. On his I have a navigation item. I can successfully change title on it.
Now I want a back button to appear - I do not need any customization at present, just the ability to catch the click and default ios look for whatever plaform the app is running (Since Swift that is ios7+).
In viewDidLoad() I have written
outletCatalogNavItem.backBarButtonItem = UIBarButtonItem(title: "test", style .plain, target: self, action: "ownbackNavigationFuncCode");
In *ViewDidAppear()* outletCatalogNavItem.backBarButtonItem is non-nil
Note: I am not using any shared navigation control across scenes in my storyboard - nor wish to at this point since I am porting an app from another tool/language which has its own navigation/stack logic already (meaning I handle switching and navigation myself in code)
Embed your view controller in a UINavigationController. When you push/segue to a new view controller the < Back button you're looking for will appear on the left hand-side.
let backItem = UIBarButtonItem()
backItem.title = "Back"
navigationItem.backBarButtonItem = backItem

Selector not working on my swift UIBarButtonItem

I'm writing a to-do list type app, in order to learn swift for iOS development. I had a settings button on the Navigation bar, on the right side, and whenever a person clicked a button on top of the table view, they could add a new item to their list. That causes a "cancel" button to appear on the left side of the bar and a "add" button to appear on the right side. When the person is done, however, those buttons disappear, and so I programatically re-create the settings button, with a call to a function that is supposed to call the segue.
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "modify"), style: UIBarButtonItemStyle.Plain, target: nil, action: Selector("showSettings:"))
This is the code that creates the button on the navbar (and it is indeed created)
func showSettings(sender: AnyObject?) {
print("segueShouldBeDone!")
performSegueWithIdentifier("showSettings", sender: sender)
}
And this is the function that is supposed to call the segue (I added the print to see if it was at least being called, and it isn't).
I've had this same problem in another place in my code, but I had given up on fixing it cause it wasn't that important for me then. But now it is interfering with how the app works, so I wanted to know how to fix it.
Any help would be great.
Your showSettings: selector isn't being called because you specified nil instead of self for the bar button item's target. Change the line to:
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "modify"), style: UIBarButtonItemStyle.Plain, target: self, action: Selector("showSettings:"))
This assume the showSettings: method is in the same class that contains this line of code.
Read the documentation for this initializer, specifically the info about the target parameter.

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

Only able to change UIBarButton title when using a hard-coded string

I have a pretty common scenario. A tree of unknown depth based on data, so I have a navigation controller with a tableView controller as the root controller. Every time I need to go deeper, I create a new tableview controller, populate it with data based on the previous selection, and push the new tableview controller onto the navigation controller stack:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
let cell = tableView.cellForRowAtIndexPath(indexPath)
let cellText = cell?.textLabel?.text
let bcti = CustomDataClass(querystring: cellText!)
if(bcti.getStatus() == "Drill")
{
let bctvc = CustomTableViewControllerClass(tableinfo: bcti)
//Note: self in this instance is my custom UITableViewController class
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: cellText, style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
self.navigationController?.pushViewController(bctvc, animated: true)
}
This works fine, a new tableview controller is created based on the selected cell from the previous controller, pushing it on the navigation stack works fine as well. The weird thing is I can't seem to get the back bar item to display the selected value from the previous controller.
If I use the cellText variable containing the text of the previously selected cell (as shown in the code above) to assign the UIBarButton title, it seems to ignore that and default to having 'Back' as the title anyway.
The cellText variable clearly has the correct value in it, I verified with NSLog statements, and also even tried conditionally unwrapping it to be 100% sure it has a value at run-time. It's being used to progress in the first place, so I think we can hopefully rule out an optional nil issue.
What's really strange to me though is that hard-coding a string into the title works just fine. So for instance if I replace:
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: cellText, style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
with:
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "Bananas", style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
It correctly displays Bananas instead of Back. I can also declare a variable and hard code that with a different value and it works fine. I.E:
let tempvar = "Bananas!"
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: tempvar, style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
It seems to only be when I try to access the text associated with the cell that it doesn't work.
Does anyone know what I'm missing here? Something else that might be worth noting is that I'm using a Storyboard to set up my initial navigation controller & tableview controller, I'm only creating the new instances of the tableview controller programmatically.
Check what the documentation of backBarButtonItem says. The cell text might simply be too long.
Instead of trying to hardcode the same values, try to change the datasource so that it has very short text values and see if it works. E.g. the cells might be displaying the first line of the multiline text now and you might have hardcoded a copy of only the first (the only visible) line. Hardcode is bad, even for tests.

Resources