MJQZ1347
MJQZ1347

Reputation: 2697

How to properly refresh a UINavigationBar?

In relation to this question: How to change Back button text from within the child view controller? I am searching for a propery way to refresh the navigation bar after changing the back button title with previousViewController.navigationItem.backBarButtonItem?.title = "New Title".

The (not so ideal?) solution from the linked question:

if let navigationController = self.navigationController {
    navigationController.popViewControllerAnimated(false)
    navigationController.pushViewController(self, animated: false)
}

Edit:

Apparently changing the layer frame forces the navigation bar to refresh. Not a solution, but a less expensive(?) workaround I guess:

if let navigationController = self.navigationController {
    navigationController.navigationBar.layer.frame.insetInPlace(dx: 0.1, dy: 0)
    navigationController.navigationBar.layer.frame.insetInPlace(dx: -0.1, dy: 0)
}

Upvotes: 8

Views: 14193

Answers (4)

ttayaa
ttayaa

Reputation: 11

UIButton *leftbtn  = [UIButton buttonWithType:UIButtonTypeCustom] ;

[leftbtn addTarget:self action:@selector(city:) forControlEvents:UIControlEventTouchUpInside];
[leftbtn setImage:[UIImage imageNamed:@"location"] forState:UIControlStateNormal];
leftbtn.contentHorizontalAlignment   = UIControlContentHorizontalAlignmentLeft;
[leftbtn sizeToFit];
self.citybtn = leftbtn;
UIBarButtonItem* cityBtn =  [[UIBarButtonItem alloc] initWithCustomView:leftbtn];

UIBarButtonItem *left_fixedSpaceBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
left_fixedSpaceBarButtonItem.width = -17;
self.navigationItem.leftBarButtonItems = @[left_fixedSpaceBarButtonItem,cityBtn];

.........

when u change

[self.citybtn setTitle:city forState:UIControlStateNormal];

[self.citybtn sizeToFit];

Upvotes: 1

Arkku
Arkku

Reputation: 42149

After trying various methods to refresh, I find this is the least ugly solution that seems to work (back then on iOS 10 but apparently not currently on iOS 13, i.e., don't count on this):

guard let navigation = navigationController,
      !(navigation.topViewController === self) else {
    return
}
let bar = navigation.navigationBar
bar.setNeedsLayout()
bar.layoutIfNeeded()
bar.setNeedsDisplay()

Other methods tried:

  • Presenting a view controller (causes screen to flicker in some cases)
  • Hiding and re-showing the bar (breaks bar if half-way between backswipe to previous VC)
  • Setting the bar's layer's frame (does not seem to work reliably, and is explicitly forbidden by the documentation for navigationBar)

Upvotes: 14

Jonesy
Jonesy

Reputation: 305

This works for me

_ = navigationController.view.snapshotView(afterScreenUpdates: true)

Upvotes: 2

tech4242
tech4242

Reputation: 2468

One solution would be to have a function, which changes the UIBarButtonItem completely by removing/hiding the back button and showing a custom UIBarButtonItem in its place with the navigationItem.leftBarButtonItem property. Surely not ideal but the button is not meant to be changed mid-VC lifecycle so I guess you could try. In that sense there is no "proper" way as this is not considered standard behaviour.

It worked for me when I added this function to a button on a sample View Controller:

func changeBackButton() {
    navigationItem.hidesBackButton = true
    navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Test", style: .plain, target: self, action: #selector(test))
}

Upvotes: -2

Related Questions