UIBarButtonItem font weight changes after push pop of navigation stack - ios

I have a UIBarButtonItem, "Done" - created in storyboard IB for ViewController A.
ViewController A is the root View Controller of the navigation stack.
If I push a View Controller B onto the navigation stack and then pop it again. the font weight of the Done button changes.
The font colour of the Done button is applied in A.viewWillAppear(..) and looks pretty much like
doneButton.tintColor = [CMPThemes navigationBarItemColour]; // it's a blue
I have stripped all appearance proxy code from the app (because there
are more than one style of navigation bars/buttons/titles appear in
the app) so I'm not looking for a fix that can only be done via the
appearance proxy...
I have checked in the debug view hierarchy that the Done button is
the same instance before and after the transitions
I have tried to re-apply the tint colour after the pop
I don't apply a font weight anywhere in the process
Also, to my eye, the font and font size seem to be unchanged during
the process.
In ViewController A
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
...
self.navigationController?.pushViewController(vcB, animated: true)
...
In ViewController B
viewDidLoad() {
...
let backButton = UIBarButtonItem(image: UIImage(named: "arrowLeft"), style: .plain, target: self, action: #selector(goBack))
backButton.tintColor = CMPThemes.popoverNavigationBarItemColour()
self.navigationItem.leftBarButtonItem = backButton
}
The storyboard looks like: (I've added the A and B to the image to maintain clarity).
If someone recognises the problem and can point me in the right direction for a fix that would be great!

I found the problem. The applied tint colour is not the issue. I think, Initially when it was loading the done button from the storyboard, the doneButton Style was equals .done and later when you are popping to ViewControllerA, somehow the style getting changed to .plan, so I think setting the style below the tint should fix the issue.
Try updating the viewWillAppear method in ViewControllerA with following code:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
doneButton.tintColor = [CMPThemes navigationBarItemColour]; // it's a blue
doneButton.style = .done
}
Hope it helps!

I have faced same issue.
I have back button custom Image and when I push view controller and press back it will getting bold (Default image)
So what I did in storyboard is apply single BLANK space to Back Button
Step 1
--> Tap on Navigation item
Step 2
--> Put blank space on back Button
Now you can observe that
Navigation item has one blank item

Related

Set a default back button on a ViewController

short: I don´t understand how to programmatically add the default back button.
long: I have been asked to write an iOS app, without any previous experience I decided
to follow the advices and code given by Nicola Zaghini.
In the code given along with the article, I really don´t understand
where does the back button come from.
The app has three screens
one to choose a city (folder WeatherLocation)
one that displays all
cities that have been already choosen (folder WeatherList)
one that
displays the weather for a city clicked in the list (folder
WeatherDetail)
There is + button for WeatherLocation:
This button is added in the code but I can not find where
and how is coded the back button in WeatherDetails (see above), and how is coded the action to do when one click the back button.
I search the web and found how to set a button in the NavigationBar:
let leftBarButtonItem: UIBarButtonItem = {
let barButtonItem = UIBarButtonItem(title: "Left Item", style: .plain, target: self, action: nil)
barButtonItem.tintColor = UIColor.red
return barButtonItem
}()
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationItem.leftBarButtonItem = leftBarButtonItem
}
I also found that there is a backBarButtonItem
but I could not find how to properly use this property.
Moreover in the code of Nicola Zaghini there is nothing like backBarButtonItem to enable the
back button, neither in the xib and storyboard.
Can someone give me some hints about on how to set the back button ?
Thanks a lot!!
In the code of Nicola Zaghini the secret to have a default back button resides (for example) in the function navigateToWeatherDetail in the class WeatherListDefaultRouter where the new ViewController is pushed on a NavigationController:
func navigateToWeatherDetail(withLocation location: Location) {
if let weatherDetailVC = self.weatherDetailBuilder()?.buildWeatherDetailModule(withLocation: location) {
self.viewController?.navigationController?.pushViewController(weatherDetailVC, animated: true)
}
}

Accessibility set focus to navigation bar title item

Overview:
I would like to set the accessibility focus to the navigation bar's title item.
By default the focus is set from top left, meaning the back button would be on focus.
I would like the title item to be in focus.
Attempts made so far:
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
navigationController?.navigationBar.items?.last)
Problem:
The above code makes no difference, the back button is still in focus.
Possible Cause:
Not able to get the item corresponding to the title to be able to set the focus.
Solution 1
I don't like it, but it was the minimum amount of hacking that does not rely on digging through hidden subviews (internal implementation of UINavigationBar view hierarchy).
First in viewWillAppear, I store a backup reference of the back button item,
and then remove the back button item (leftBarButtonItem):
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
backButtonBackup = self.navigationItem.leftBarButtonItem
self.navigationItem.leftBarButtonItem = nil
}
Then I restore the back item, but only after I dispatch the screen changed event in viewDidAppear() :
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, nil)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in
self?.navigationItem.leftBarButtonItem = self?.backButtonBackup
}
}
Solution 2:
Disable all accessibility on the nav bar and view controller up until viewDidAppear() is finished:
self.navigationController.navigationBar.accessibilityElementsHidden = true
self.view.accessibilityElementsHidden = true
, and then in viewDidAppear manually dispatching the layout element accessibility focused event to the label subview of UINavigationBar:
UIAccessibilityPostNotification( UIAccessibilityLayoutChangedNotification, self.navigationController.navigationBar.subviews[2].subviews[1])
// The label buried inside the nav bar. Not tested on all iOS versions.
// Alternately you can go digging for the label by checking class types.
// Then use DispatchAsync, to re-enable accessibility on the view and nav bar again...
I'm not a fan of this method either.
DispatchAsync delay in viewDidAppear seems to be needed in any case - and I think both solutions are still horrible.
I invoked UIAccessibilityScreenChangedNotification on navigation title from viewDidLoad and it worked
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification,
self.navigationItem.title);
First, we'll need to create an useful extension:
extension UIViewController {
func setAccessibilityFocus(in view: UIView) {
UIAccessibility.post(notification: .screenChanged, argument: view)
}
}
Then, we'll be able to set our focus in the navigation bar title like this:
setAccessibilityFocus(in: self.navigationController!.navigationBar.subviews[2].subviews[1])

iOS: Back button label is cut off with custom font

I'm using a custom font for the navigationItem; When I segue to another view controller the back button on the newly presented view controller is cut off on the left side. I have tried setTitlePositionAdjustment(_ adjustment: UIOffset, for barMetrics: UIBarMetrics) on the first view controller, before doing segue but it didn't displace the button:
And
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "Show Contents" {
if let viewController = segue.destination as? ContentsTableViewController {
viewController.navigationItem.backBarButtonItem?.setTitlePositionAdjustment(UIOffsetMake(10, 0), for: .default)
}
}
}
Using this code:
override func viewDidLoad() {
super.viewDidLoad()
let backBarButtonItem = UIBarButtonItem(title: "خانه", style: .plain, target: nil, action: nil)
backBarButtonItem.setTitleTextAttributes([.font : UIFont(name: "B Koodak", size: 32)!], for: .normal)
navigationItem.backBarButtonItem = backBarButtonItem
}
I get the following:
I encountered this issue when was using a custom font and I was hiding the navigation bar in the parent view and showing it in child views (in viewWillAppear(_:)). Figuring that something in this action was causing the button label to get drawn too narrow, I tried calling various UIKit redrawing methods on the navigation bar (setNeedsLayout() & setNeedsDisplay()) in the child view controllers' viewDidLoad() methods, but to no avail. I was able to get the label to draw properly by making the font size smaller, as the OP wrote.
I was eventually able to get it to draw properly at the correct size by manually setting the backBarButtonItem property to a new instance of UIBarButtonItem with "Back" as the title (in my parent view controller). This is possibly why it worked in the accepted answer. This seems to be a bug in UIKit, as I wouldn't think that hiding and showing the nav bar would cause this behavior.

Making only one navigation bar in Navigation Stack transparent iOS Swift

I have a navigationcontroller with navigation flows as shown below:
NC -> A -> B
B appears through a push segue.
The navigationbar of A is made transparent using following
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.navigationController!.navigationBar.isTranslucent = true
self.navigationController!.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController!.navigationBar.shadowImage = UIImage()
}
and is translucency is set to false in viewWillDisappear so that B can have the usual navigation bar:
override func viewWillDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
self.navigationController!.navigationBar.isTranslucent = false
}
The issue is that when Back button is pressed in B to return to A, The navigation bar of B appears momentarily before disappearing. How to solve this issue?
PS: I do not want to add code to overridden methods of B as B might be shared by other navigation controller.
The issue is that when Back button is pressed in B to return to A, The
navigation bar of B appears momentarily before disappearing. How to
solve this issue?
You do not need to toggle anything in your viewWillDisappear method. Just toggle everything in your viewWillAppear method in your every screens.
Is this what you want? If so, I made a sample project on Github just for you, and for other people who are new to iOS in the future.
https://github.com/glennposadas/showhidenavbar-ios
Though it uses my very simple cocoapod, you can just copy everything from my framework and sample project.

position title of UINavigationBar to be at the center horizentally and vertically

I have hard time to find how to do this simple thing on internet so I ask here.
I have a UINavigationController, which contains a UINavigationBar. By default if my app goes to 2nd embedded UIViewController the navigation bar shows a back button and a title. But the title is not positioned in the center horizentally.
How to make the title of UINavigationBar to be positioned always at the center horizentally & vertically?
What I meant is how to make the "Paid invoices" next to back button to be at the center of navigation bar?
It is default behaviour when you are pushing ViewController with UINavigationController it will show the title of previous screen with back button
However, You can modify back button title with
self.navigationItem.backBarButtonItem?.title = ""
and
set title like self.title = "Paid invoices""
it will show your title in center
Or you want to put custom back button image then
call it from ViewDidLoad of pushed view controller
func setBackButtonWithArrow() {
self.navigationItem.setLeftBarButton(UIBarButtonItem.init(image: UIImage.init(named: "back_nav_White_arrow"), style: .plain, target: self, action: #selector(popVC)), animated: true)
}
func popVC () {
self.navigationController!.popViewController(animated: true)
}

Resources