Back Button Title changes when setting View Controller Title - ios

I have a UIViewController A, embedded in a UINavigationViewController that pushes another view controller B on some action. Given the UIViewController.title of A is set to say "VC A", when B is displayed the back button has the title "< VC A" as expected. However if I were to set the title of B AFTER its viewDidLoad, the back button title gets changed to "< Back". If the title were to be set in viewDidLoad the back button title remains as "< VC A". Any thoughts on how I can set the title of B without it effecting the back button?
class ViewControllerA: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = "VC A"
}
func doSomething() {
// Instantiate ViewControllerB
navigationController?.pushViewController(viewControllerB, animated: true)
}
}
class ViewControllerB: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
title = "VC B" // Works as expected
}
func doSomething() {
title = "VC B" // Causes the back button title to change to Back from VC A
}
}
I can create a custom back button with the title of the previous view controller in the navigation stack as a workaround but I'm rather curious as to why this occurs in the first place and a possible simpler work around.

Related

Navigation Item Title not Showing

I have following code in class homeVC: UICollectionViewController to display the title in the navigation bar and it works as expected.
override func viewDidLoad() {
super.viewDidLoad()
.
.
.
self.navigationItem.title = PFUser.current()?.username?.uppercased()
.
.
}
But when I clicked on the button to go to another view controller and then came back to homeVC view controller self.navigationItem.title is not displaying anything. I'mm wondering why that happened?
The following code is to go to another view controller
#objc func followersTap() {
category = "followers"
// make references to followersVC
let followers = self.storyboard?.instantiateViewController(withIdentifier: "followersVC") as! followersVC
// present
self.navigationController?.pushViewController(followers, animated: true)
}
This is the view controller to go to , and in this view controller i don't want to show the title from previous view controller so I'm using self.navigationController!.navigationBar.topItem!.title = "" to make it empty and when I go back to previous view controller the title is also empty, why?
class followersVC: UITableViewController{
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController!.navigationBar.topItem!.title = ""
self.tableView.reloadData()
}
I am trying to not show the first view controller title on second view controller top left corner where back button is and keep first and second view controller title shows.
If your plan is to not show the title from a previous view controller, the solution is like this:
Change:
self.navigationItem.title = PFUser.current()?.username?.uppercased()
to:
self.title = PFUser.current()?.username?.uppercased()
And change:
self.navigationController!.navigationBar.topItem!.title = ""
to:
self.title = ""
Try to move your self.navigationItem.title = PFUser.current()?.username?.uppercased() in viewDidAppear(), this should help

Changing the title value of button that takes you to the previous view controller

I have multiple view controllers embedded inside a navigation controller. I have an option that changes the language of the user interface. When user chooses French, the user interface should update itself with French language. All elements are updated with French as I expected, except the button that takes user back to the previous view controller (as you can see in the screenshot - "List of Events").
This is the function I call to update UI :
func updateView() {
DispatchQueue.main.async {
// CurrentSelection.LanguageUI holds a reference to currenly selected language object
navigationItem.title = CurrentSelection.languageUI.event_singular
navigationController?.title = CurrentSelection.languageUI.listOfEvents <<- this line doesn't work
}
}
The navigationController?.title holds "List of Events" string and the assignment statement seems to work. It just that the UI isn't updated with the new title value. Where am I doing wrong?
It's the previousVC that decides what it's own back button will be.
So set this on the previousVC in viewDidLoad
self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:#"Back title" style:UIBarButtonItemStylePlain target:nil action:nil];
If this isn't set then the back button will be the .title of the previous VC.
-
In your situation you should have some kind of language change notification that the previous VC can listen for and know to update its own title.
One way to do it is to implement UINavigationControllerDelegate and set the back bar button item there:
class YourViewController: UIViewController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
navigationController?.delegate = self
}
// MARK: - Navigation controller delegate
public func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
let item = UIBarButtonItem(title: "Custom title", style: .plain, target: nil, action: nil)
viewController.navigationItem.backBarButtonItem = item
}
}
Something to be aware of is that this needs to be implemented in the view controller which pushes the next view controller. So you'll be changing the back bar button item of the view controller that willShow.
Those view controllers in the navigation stack won't change atomatically.
So you have to use Notification center and add observers to the previous view controllers and change the Title of the navigation bar with selected language when it is called.
When language is changed you have to post the change.
Then it will change the title to french so does the back button will
change.

What's the different with titles?

I would like to add title to my UIViewController:
I have tried
self.navigationController.navigationItem.title
self.navigationController.title
self.title
Sometimes, solution 1 work, sometimes solution 2 work, sometimes solution 3 works.
Can any expert tell me the different between them?
title is a property of UIViewController.
A localized string that represents the view this controller manages.
Set the title to a human-readable string that describes the view. If
the view controller has a valid navigation item or tab-bar item,
assigning a value to this property updates the title text in those
objects.
self.navigationController is a UINavigationController that manages the stack of viewControllers that your viewController is in. UINavigationController is a subclass of UIViewController, so self.navigationController.title is the title of the UINavigationController.
self.navigationItem.title:
The navigation item’s title displayed in the center of the navigation
bar. The default value is nil. When the receiver is on the navigation
item stack and is second from the top—in other words, its view
controller manages the views that the user would navigate back to—the
value in this property is used for the back button on the top-most
navigation bar. If the value of this property is nil, the system uses
the string “Back” as the text of the back button.
So, in practice, you should set the title of your ViewControllers. iOS will copy this title to the navigation item or tab bar item and display this title on the Navigation Bar if your ViewController is managed by a UINavigationController, it will use that text for the Back Button when you push to another ViewController, and it will display it in the tab bar if your ViewController is managed by a UITabBarController.
I create a demo to explain them:
You see, my vc1 is yellow-gray color embed in a navigation controller,my vc2 is light-green color embed in a navigation controller too, the two navigation controller is all manage by a tabbar controller .
In ViewController.swift(it is vc1), if I set self.title:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "vc1's title"
}
}
In ViewController2.swift(it is vc2):
import UIKit
class ViewController2: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.title = "vc2's title"
}
}
The result is the tabbar title and navigation title all set:
If I set self.navigationController?.title:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// self.title = "vc1's title"
self.navigationController?.title = "vc1's nav title"
}
}
The result is tabbar title is set:
If I set self.navigationItem.title:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// self.title = "vc1's title"
//self.navigationController?.title = "vc1's nav title"
self.navigationItem.title = "vc1's navItem title"
}
}
The result is navigation title is set:

Remove "Back" text from navigation controller [duplicate]

This question already has answers here:
Removing the title text of an iOS UIBarButtonItem
(39 answers)
Closed 7 years ago.
I do not want to see the "Back" text that is displayed automatically when going to a view controller with no Title. I would like this to happen everywhere on my application, is there a few lines of code I can put in the app delegate to make this possible?
I have tried a few approaches from SO before posting this question and have found no success. I want to this in Swift, not Obj-c.
I tried this with no success; it ran fine, but the back text was still displayed in the next view controller.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let backItem = UIBarButtonItem()
backItem.title = "Something Else"
navigationItem.backBarButtonItem = backItem // This will show in the next view controller being pushed
}
I would like to put this in app delegate so it would happen throughout my application rather than having to put that into every single swift file in my project. Anybody have any ideas?
There are several different ways to do this. I’ll present a solution that gives you a bit more flexability as you’ll want to display a button some times, hide other times, and customize at other times. This example assumes you are using Navigation Controller based UI, but can be adapted to other types.
Whenever I create an app I like to create my own UIViewController class and have all of my UIViewControllers inherit from this single view controller. If I want to customize something for all my UIViewControllers I can do this at my new super class level since all the other views inherit from that.
In my sample code below I create my custom UIViewController called MasterViewController. I have all of my UIViewControllers inherit from it: ViewController, ViewController2 & ViewController3. Read the notes in the code below to understand what’s going on.
NOTE: Make sure you check for the case where the UIViewController should not have the back button as this would be the case where there is nothing to return to. You can add this code to MasterViewController but I did not for this sample.
//=========================================================
//MasterViewController
//=========================================================
// Master View Controller that all UIViewControllers inherit from.
import UIKit
class MasterViewController: UIViewController {
// This will be title if the user doesn't change it in a subclass.
var backButtonTitle = "Default Back Title"
override func viewDidLoad() {
// This resets the default back button to always be shown unless the user calls createCustomBackButtonWithTitle from child class.
self.navigationItem.hidesBackButton = false
super.viewDidLoad()
}
func createCustomBackButtonWithTitle(customTitle: String) {
// Hide the default back button.
self.navigationItem.hidesBackButton = true
// Programmatically create custom back button.
let backButton = UIBarButtonItem(title: customTitle, style: UIBarButtonItemStyle.Plain, target: self, action: "goBack:")
self.navigationItem.leftBarButtonItem = backButton
}
#IBAction func goBack(sender: UIButton!) {
navigationController?.popViewControllerAnimated(true)
}
}
//=========================================================
//ViewController
//=========================================================
// This view controller customizes the back name and inherits the default "go back" behavior from MasterViewController.
import UIKit
class ViewController: MasterViewController {
override func viewDidLoad() {
super.viewDidLoad()
createCustomBackButtonWithTitle("ButtonName")
}
}
//=========================================================
//ViewController2
//=========================================================
// This view controller customizes the back name but overrides the default "goBack" behavior from MasterViewController to do something different
import UIKit
class ViewController2: MasterViewController {
override func viewDidLoad() {
super.viewDidLoad()
createCustomBackButtonWithTitle("ButtonName 2")
}
// Override the back button behavior from super class because we don't want default "back" behavior.
#IBAction override func goBack(sender: UIButton!) {
// Costume Code here
}
}
//=========================================================
//ViewController3
//=========================================================
// This view controller inherits from MasterViewController, but gets the default "back" behavior from a normal
// UINavigationController since it never calls createCustomBackButtonWithTitle to change the behavior
import UIKit
class ViewController3: MasterViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
}
Most people wind up doing something like #xdeleon suggested, but I highly recommend not doing that, if for no other reason that that you'll break the built-in swipe behavior to navigate backwards in the view controller stack. There are a couple answers on SO that tell you how to restore this back-swipe functionality, but as someone who's had to fix this in an existing app with 200+ view controllers, I'd like to save you a lot of pain.
If you don't mind working in storyboards, then make sure that each of your view controllers' navigation item's back button text is an empty string. However, since that's a lot to keep track of, it's marginally easier to do in code, and the only way I've found works perfectly is to create a cachedTitle instance variable on your view controller, and implement the view controller's viewWillAppear() and viewWillDisappear() methods like so:
override func viewWillAppear(animated: Bool) {
// Reset the view controller's title only if it doesn't already have one.
if (navigationItem.title == nil || navigationItem.title == "") && navigationItem.titleView == nil {
navigationItem.title = cachedTitle
}
}
override func viewWillDisappear(animated: Bool) {
if navigationItem.title != nil { // not 'if let'!
cachedTitle = navigationItem.title
navigationItem.title == ""
}
}
You can either put these methods in a UIViewController subclass that all of your view controllers extend, or, better yet, put these in custom methods in a category on UIViewController, and just call them in your view controllers' viewWillAppear() and viewWillDisappear() calls.
If you want to use something other than the back button's '<' symbol, don't add a custom back or left button; just set a custom back indicator image and mask on the navigation bar.

Can't get any navigation item in iOS

I have a question regarding the navigation bar.
As far as I understand from iOS: A view controller opened by a segue inherits the navigation bar of the parent view controller. Is this correct so far?
Is there a view controller within a stack "owns" the navigation bar in a complex segue stack (e.g. TableViewController that opens a TabBarController that opens ...)?
I very often run into the problem that I don't know where to get the actual navigation item in order to set the title or a bar button item.
In this case, I have the following controllers:
TabBarController
EventPostsViewController -> To display a list of posts, is a tabbed view within the TabBarController
CreatePostViewController -> To write a new post
So within the EventPostsViewController I can do this (and it works):
class EventPostsViewController: UITableViewController {
...
override func viewWillAppear(animated: Bool) {
...
// This solution works, but only for EventPostsViewController
self.tabBarController?.navigationItem.title = "text"
But within the CreatePostViewController, which is opened by a segue via EventPostsViewController, neither of this solutions work.
class CreatePostViewController: UIViewController {
...
override func viewWillAppear(animated: Bool) {
...
// Neither of these solutions works
self.navigationItem.title = "Text"
self.tabBarController?.navigationItem.title = "Text"
self.navigationController?.navigationItem.title = "Text"
How do I get the actual navigation bar/navigationItem?
Stupid simple mistake I repeat every time :)
I forgot to link my custom CreatePostViewController with the view controller using the interface builder.
This code now works:
class CreatePostViewController: UIViewController {
...
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated);
self.navigationController?.setNavigationBarHidden(false, animated: false)
// Set title
self.navigationItem.title = "Write Post"
// Add Submit button
var submitButton = UIBarButtonItem(barButtonSystemItem: UIBarButtonSystemItem.Done, target: self, action: "submitPost:")
self.navigationItem.rightBarButtonItem = submitButton
}
...
}

Resources