Custom navigation back button with custom title in swift3 - ios

I need to implement the navigation bar with custom back image and custom title. For example: if i am in "Login with facebook" page i need to show the "Login with facebook" besides the custom back image. For this i am following the code like this:
let yourBackImage = UIImage(named: "back_button_image")
self.navigationController?.navigationBar.backIndicatorImage = yourBackImage
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = yourBackImage
self.navigationController?.navigationBar.backItem?.title = "Custom"
But I am getting the output like this:
Note: The title is of the current page.
How to achieve this?

If the back title is too long iOS will change it to 'Back'.
However if you add a left bar button item - this you can set to a longer length.
long back
From Apple docs: https://developer.apple.com/documentation/uikit/uinavigationcontroller
In cases where the title of a back button is too long to fit in the available space, the navigation bar may substitute the string “Back” for the actual button title. The navigation bar does this only if the back button is provided by the previous view controller. If the new top-level view controller has a custom left bar button item—an object in the
leftBarButtonItem
or
leftBarButtonItems
property of its navigation item—the navigation bar does not change the button title.
To do this go to the Storyboard, in Interface Builder, drag a Bar Button Item into your navigation bar and set its title.
*Note that you will need to hook this up to your view controller to pop it.
#IBAction func pop(_ sender: UIBarButtonItem) {
_ = navigationController?.popViewController(animated: true)
}

You'll find a lot of StackOverflow attempts to answer this (quite common) issue, and the best way that I've found is also rather complicated. You'll have to do this for every UINavigationController, UINavigationBar, and UINavigationItem in your app, and that's painful.
How Apple wants you to do it (as far as I can tell)
For UINavigationBars in a storyboard, set the Back and Back Mask values (in the Attributes inspector) to the name of your custom image.
For every view controller's UINavigationItem, set its Back attribute value to an empty string or single space.
Easy enough, right? Nope. For one thing, the empty string or single-space Back title isn't always honored. And what if you have dozens, or even hundreds, of view controllers? What if your application has multiple UINavigationControllers? What if you set configure your view controllers (or at least their navigation items) programmatically? Then it's not so easy.
How StackOverflow thinks you should do it
I'm not going to link to all of the SO answers that recommend this way of doing things. I'll just summarize it as:
Hide the navigation item's back button with UINavigationItem.setHidesBackBarItem(_:animated:).
Create a UIBarButtonItem with your custom back image and empty title, then add it to your navigation item's leftBarButtonItem.
Sit back and wait for user bug reports to flood in with complaints that your app doesn't support swiping to navigate back.
Or, if you want to be clever, attach your navigation controller's interactivePopGestureRecognizer to your custom left bar button item. This usually works, but it feels like a hack that's likely to break in future releases. If you're ok with that, then you can stop reading right here.
How I think you should do it
If you have a lot of view controllers (our app has 200+) and/or a mix of VCs configured programmatically and in storyboards, this may ultimately be an easier way.
Create a subclass of UINavigationBar and set this as the class for all navigation bars in your storyboards and code. (If you're doing it in code, note that the nav bar is a read-only property; it can be set only in the navigation controller's initializer.)
The UINavigationBar manages a stack of UINavigationItems, one for each view controller on the UINavigationController's stack, and keeps them in sync with their view controllers. In other words, when you push a view controller onto the nav controller, that view controller's nav item also gets pushed onto the nav bar, and it gets popped when the VC is popped. When the navigation bar pushes or pops an item, it uses the new topmost item's title, titleView, and/or button item properties to set configure its own title view and buttons, and uses the title of the navigation item immediately below the topmost one to configure its back button and back image. If the next-to-topmost item's title is nil, then the nav bar uses the default value of "Back". You don't want that. If you can set the Back attribute to an empty or single-space string in the storyboard, then you should be able to do the same thing in code, right? Wrong. The back text is not a public property in UINavigationItem! WTF?
Instead, you have to set the title of the next-to-topmost item to an empty string. "But wait!" you say, "Doesn't that permanently change the next-to-topmost item's title, so that when I navigate back to that view controller, its title will say "Back" instead of its original title?" And you'd be right. What can you do? In your subclass of UINavigationBar, add an array of Optional title strings, like so:
var navigationItemTitles = [String?]()
(Why Optional? Because navigation items may have a custom titleView instead of a title.)
Override the navigation bar's pushNavigationItem(_:animated:) and popNavigationItemAnimated(_:) so that they modify and restore the navigation items' titles, respectively:
override func pushNavigationItem(_ item: UINavigationItem, animated: Bool) {
super.pushNavigationItem(item, animated: animated)
// Store the new back item's title, then set its title to
// an empty string.
if let backItem = backItem {
navigationItemTitles.push(backItem.title)
backItem.title = ""
}
}
override func popNavigationItemAnimated(_ animated: Bool) {
super.popNavigationItemAnimated(animated)
// Restore the new top item's title.
if navigationItemTitles.count > 0 {
topItem?.title = navigationItemTitles.popLast
}
}
Caveats
This is the only way I've found that's foolproof and not hacky. If Apple simply made the navigation item's Back property publicly settable, then a lot of this could be avoided.
(For what its worth, not only does our app have 200+ view controllers, it also has multiple navigation controllers with different navigation bar looks and feels. That's a whole other layer of complexity.)

Related

Automatically add "back" button in navigation bar

First of all: I'm using Swift 5 with Xcode 10 (iOS 12.0).
I'm pretty new to Swift/iOS development and watched a couple of videos on how to add a "back" button to the navigation bar. In all of them (with Swift 3) the button just magically appears once the app is run after adding a segue (between an item in the first scene and the second scene) and a title for both navigation bars.
My app has three ViewControllers/scenes:
Login without navigation bar ("login" button: segue to scene 2)
Table View (linked to scene 3 with segue, so I can just tap on an item in the list)
Further information for a single item in the Table View
The segue between scene 2 and 3 is set to "show (e.g. push)" because "push" is deprecated and crashes the app.
I added both navigation bars to the navigation controllers (2 & 3) by hand and want to add an arrow icon to the navigation bar of scene 3. What I've tried so far:
Add titles for the navigation bar in both scenes
Set the "Back button" attribute (Navigation bar text), which created a child object
Then set the child object's "Image" attribute
Set the "Back" and "Back Mask" attribute (Navigation bar) to the same image
Nothing shows up though, neither in Xcode nor in the simulator (not even the image).
Is it even still possible to get an automatic "back" button or do you now have to add it yourself using a "Bar Button Item"?
So when you show a View Controller such as your v2,v3 those View Controllers are no longer in the same navigation stack as your v1, that is why it is not automatically showing the back button.
I don't know why it is saying your Push function is deprecated but if you could grab a screenshot maybe I can give you a reason why. I am using Swift5 and I'm able to use this : self.navigationController?.pushViewController(vc, animated: true)
To answer your question, if you want to add a back button to v2,v3 then you DO have to use BarButtonItem, and call
self.dismiss(viewController, true) I strongly recommend you not do it this way if your intent is to have them in the same Navigation stack as your v1.
Answering my own question: Yes, it's still (Xcode 10/Swift 5) possible to make it add a "back" button automatically.
Push segues are deprecated since iOS 8, so use Show (e.g. Push) instead!
Usually a Show (e.g. Push) segue uses Show but if there's a NavigationController somewhere to the left (scenes going from left to right) of the current ViewController, it automatically uses Push, which also adds a "back" button to the NavigationBar of the next scene or, if there's no NavigationBar, it simply adds the button where the bar would usually be.
My scenes now look like this:
ViewController1: Login without NavigationBar ("login" button: segue to NavigationController)
NavigationController: With automatic segue to a new UITableViewController (if added through the library)
ViewController2: Table View (linked to 4. with segue)
ViewController3: Further information for a single item
Adding data to the segues between 1. and 3. (using 2.) is handled a little bit different than it would be without NavigationController:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let navC = segue.destination as? UINavigationController
let vc2 = navC?.viewControllers.first as! ViewController2
vc2.myinformation = myinfosArray
}
(Bad) Alternative to a NavigationController:
Add a NavigationBar and a BarButtonItem and link it to the previous view like you would any other button.
Disadvantage: The view (in my case "ViewController2") resets once you switch to the next one (because the two views aren't on the same stack, see jaelee's explanation above), so if you go back you have to set everything up again.

Display View Title on Next Line if Back Button Text Too Long

This is my first day trying to write an iOS app, having followed the tutorials on the Apple site and a few others, so be gentle. It's actually going pretty well so far!
My main view is a list of company contacts. Tapping on a contact segues to a view showing that contact's details. I've embedded my views in a Navigation view, and the navigation all works fine. On the contact details view, the view title (self.title) is being set to the person's name in the viewDidLoad function.
The problem I've got is that the text of the 'back' navigation button in the title bar on the contact details view is being derived from the title of the main view, as expected, but that text is long enough to push the title of the contact detail view - the person's name - to the right, like this:
I was wondering if this is the expected and correct behaviour for an iOS app or if I should attempt to have the contact detail view's title displayed on a different line to the back navigation button, and, if so, how I would do that.
I know that I can override the text of the back navigation button (indeed, it's automatically set to "Back" if my main controller's title is set to something a bit longer, interestingly), but that's not going to solve the problem if a person has a particularly long name!
What you're seeing is indeed the correct/expected behavior.
If you don't like that back button text, you can always change or remove it:
let backButtonItem: UIBarButtonItem = UIBarButtonItem()
backButtonItem.title = "" // Set your custom text here
navigationItem.backBarButtonItem = backButtonItem
If you want to do a multi-line title you'll have to customize it by either creating your own UINavigationBar subclass or by setting a custom titleView on your UINavigationItem and setting a custom view in which you render multiple lines.

Add another button to navigation bar

I'm modifying an app and this app has some buttons in its navigation bar, I wanted to add another button or Bar Button item by dragging it to the hierarchy but it seems I can't add it as a child to navigation bar, I wanted to ask if you have any solutions to this, here's a snapshot of the hierarchy of the storyboard, I want to add another Bar Button Item or another Button to the Navigation item.
Thanks
So I've just created a blank project and I can do this:
I've also done stuff like this before where I actually use a UIButton rather than a bar button item gaining some extra customisability. Just make sure that you set an outlet to the button and in the view controller call self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:self.customButton];:
You can have multiple right bar button items and if they overlap the title (if you have one) then they are essentially clipped. From my understanding, you can have as many buttons as you like until the space is gone (which can vary depending on the device, obviously). Relevant docs:
This array can contain 0 or more bar button items to display on the right side of the
navigation bar. Items are displayed right-to-left in the same order as they appear in the array. Thus, the first item in the array is the rightmost item and other items are added to the left of the previous item.
If there is not enough room to display all of the items in the array, those that would overlap the title view (if present) or the buttons on the left side of the bar are not
displayed.
An example with lots of buttons:
Programatically you can add buttons using:
navigationItem.rightBarButtonItems = [aButton, anotherButton, awesomeButton]
Just make sure you test that the smallest device you target will still provide a good UX for the user if you add lots of buttons.
here is the correct answer for swift :
let barButton_array: [UIBarButtonItem] = [Button1, Button2]
navigationItem.setRightBarButtonItems(barButton_array, animated: false)
Seems you can only have 3 elements on navigation bar with Interface Builder and you have to add more buttons via code. This link worked for me :
http://mandarapte.com/apple/add-navigation-bar-buttons-ios-app-xcode-project/

Is there a way to hide the text on the back button in UINavigationBar universally?

Seemingly the accepted way of changing the back button text for a UIViewController's navigation bar is this answer, essentially setting the text to nothing on the previous view controller.
However, this has to be done on every view controller. Worse yet, the button still takes up quite a bit of horizontal space (past what you would logically expect) as if there's still text there.
Is there a way, say in a UINavigationController subclass to hide the back button text in every view controller and make the area occupied by the back button more or less just the size of the "<"?
Create a subclass of UINavigationBar. In your subclass, override backItem to return a UINavigationItem with an empty title. You might have to use a string containing a space rather than the empty string.
Specify that subclass as the custom class of the navigation bar in your storyboard, or (if you create the navigation controller in code) using initWithNavigationBarClass:toolbarClass:.

Questions about navigation bar

I am confused about the relationship of navigation controller and content view controllers. In storyboard, navigation bar is under the navigation controller, so I linked it to the NavigationController class and then customized the navigation bar in the viewDidLoad function. However, since different content views will have different navigation bar, like different bar buttons, how can I realize this? Just some basic ideas are good enough. My guess is that I need to specify specific bar buttons in specific content view controllers, but since the navigationBar property is in NavigationController class, how can I refer to it?
I want to hide the title of the navigation bar and make the bar only show some custom buttons (I know toolbar may match it better, but I have other reasons to adopt navigation bar). Please tell me how to hide the title in detail and swift language is preferred.
1) Every ViewController instance has a navigationController property, it' just an optional. To set the bar buttons, you'll want to use navigationItem. So to specify bar buttons you either do so in Interface builder per view controller, or just or do something like this in viewDidLoad
let navBarButton = UIBarButtonItem()...set up bar button
self.navigationItem?.leftBarButtonItem = navBarButton
2) Hiding the title is trivial:
self.navigationItem?.title = ""

Resources