Swift Custom NavBar Back Button Image and Text - ios

I need to customise the look of a back button in a Swift project.
Here's what I have:
Here's what I want:
I've tried creating my own UIBarButtonItem but I can't figure out how to get the image to be beside the text, rather than as a background or a replacement for the text.
let backButton = UIBarButtonItem(title: "Custom", style: .Plain, target: self, action: nil )
//backButton.image = UIImage(named: "imageName") //Replaces title
backButton.setBackgroundImage(UIImage(named: "imageName"), forState: .Normal, barMetrics: .Default) // Stretches image
navigationItem.setLeftBarButtonItem(backButton, animated: false)

You can do something like that:
let yourBackImage = UIImage(named: "back_button_image")
self.navigationController?.navigationBar.backIndicatorImage = yourBackImage
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = yourBackImage
self.navigationController?.navigationBar.backItem?.title = "Custom"
Your image will only have one color though

Note: Please remember that the back button belongs to the the source ViewController and not to the destination ViewController. Thus, the modification needs to be done in the source VC, which is reflected to all the view in the navigation controller
Code Snippet:
let backImage = UIImage(named: "icon-back")
self.navigationController?.navigationBar.backIndicatorImage = backImage
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImage
/*** If needed Assign Title Here ***/
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.plain, target: nil, action: nil)

swift 4
In my case, I needed to have only the image of the button, without any text. I hope this will be useful to someone.
let imgBackArrow = UIImage(named: "back_arrow_32")
navigationController?.navigationBar.backIndicatorImage = imgBackArrow
navigationController?.navigationBar.backIndicatorTransitionMaskImage = imgBackArrow
navigationItem.leftItemsSupplementBackButton = true
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: self, action: nil)
For iOS 12 you can do
func setNavigationBar() {
self.navigationItem.setHidesBackButton(true, animated:false)
//your custom view for back image with custom size
let view = UIView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
let imageView = UIImageView(frame: CGRect(x: 10, y: 10, width: 20, height: 20))
if let imgBackArrow = UIImage(named: "icn_back_arrow") {
imageView.image = imgBackArrow
}
view.addSubview(imageView)
let backTap = UITapGestureRecognizer(target: self, action: #selector(backToMain))
view?.addGestureRecognizer(backTap)
let leftBarButtonItem = UIBarButtonItem(customView: view ?? UIView())
self.navigationItem.leftBarButtonItem = leftBarButtonItem
}
#objc func backToMain() {
self.navigationController?.popViewController(animated: true)
}

For setting custom back bar button and remove text from back bar button,
FROM STORYBOARD only, without any coding.
RESULT:

For the back button image:
By this tutorial: (but didn't work for me)
UINavigationBar.appearance().backIndicatorImage = UIImage(named: "imageName")
But this stack answer: (worked for me)
var backButtonImage = UIImage(named: "back-button-image")
backButtonImage = backButtonImage?.stretchableImage(withLeftCapWidth: 15, topCapHeight: 30)
UIBarButtonItem.appearance().setBackButtonBackgroundImage(backButtonImage, for: .normal, barMetrics: .default)
And for the font, assuming you want the font to match for the whole navigation bar:(currently in use)
if let font = UIFont(name: "Avenir-Book", size: 22) {
UINavigationBar.appearance().titleTextAttributes = [NSFontAttributeName: font]
}

Having a button in Navigation Bar with Image AND Text is quite hard. Especially after they have introduced a new headache with UIBarButtonItem position in iOS 11: iOS 11 - UIBarButtonItem horizontal position
You can make either button with image or a button with text, but not a button with both of those. I even tried two UIBarButtonItems together, one with image and other with text - it still doesn't look good at all and their UIStackView can't be easily accessed for modification.
Unexpectedly I found a plain simple solution:
1) design the button as view in Interface Builder. In my case it is inside target UIViewController and accessible via IBOutlet for simplicity
2) set Leading Space constraint for the image to be negative, you might also want to set view's background color to .clear.
3) use it:
#IBOutlet var backButtonView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
let backButton = UIBarButtonItem(customView: self.backButtonView)
self.backButtonView.heightAnchor.constraint(equalToConstant: 44).isActive = true // if you set more than you'll get "Unable to simultaneously..."
self.backButtonView.widthAnchor.constraint(equalToConstant: 75).isActive = true
self.navigationItem.leftBarButtonItem = backButton
}
That's it. No need to use the trick with negative spacer for iOS 10 or the trick with imageInsets for iOS 11 (which works only if you have image and doesn't work for image+text, BTW).

I have tried all the above and all make the custom image without changing the text
The only one worked for me is from this answer
let backBTN = UIBarButtonItem(image: UIImage(named: "Back"),
style: .plain,
target: navigationController,
action: #selector(UINavigationController.popViewController(animated:)))
navigationItem.leftBarButtonItem = backBTN
navigationController?.interactivePopGestureRecognizer?.delegate = self

swift 3
extension UIViewController {
func setBackButton(){
let yourBackImage = UIImage(named: "backbutton")
navigationController?.navigationBar.backIndicatorImage = yourBackImage
navigationController?.navigationBar.backIndicatorTransitionMaskImage = yourBackImage
}
}

This worked for me on iOS 13 using swift 5. Just hide the original back button and add a new navigation left bar button item with an action.
navigationItem.hidesBackButton = true
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "backBtn"), style: .plain, target: self, action: #selector(back(sender:)))
#objc func back(sender: UIBarButtonItem) {
self.navigationController?.popViewController(animated:true)
}

I know it was answered. Here you can set title, image and target.
let view = UIView()
let button = UIButton(type: .system)
button.setImage(UIImage(named: "backArrow_theme"), for: .normal)
button.setTitle("Back to workflow", for: .normal)
button.addTarget(self, action: #selector(onBackButton(_:)), for: .touchUpInside)
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: -10)
button.sizeToFit()
view.addSubview(button)
view.frame = button.bounds
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: view)

Just replace the backButton with a custom rightBarButtonItem
let backImage = UIImage(named: "BackBtn")?.withRenderingMode(.alwaysOriginal)
navigationItem.leftBarButtonItem = UIBarButtonItem(image: backImage, style: .plain, target: self, action: #selector(popnav))
#objc func popnav() {
self.navigationController?.popViewController(animated: true)
}

Just in case someone need to change all Back buttons color or font with Swift5. UIBarButtonItem.appearance().tintColor = .red
Add this to AppDelegate.swift file.
import UIKit
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UIBarButtonItem.appearance().tintColor = .white
UIBarButtonItem.appearance().setTitleTextAttributes([
NSAttributedString.Key.foregroundColor: .red,
NSAttributedString.Key.font: UIFont(name: "font_name", size: 14)!
], for: .normal)
return true
}
}

iOS13 And Later, Try to use UINavigationBarAppearance
let appearance = UINavigationBarAppearance()
// set back image
appearance.setBackIndicatorImage(UIImage(named: "back_icon"), transitionMaskImage: UIImage(named: "back_icon"))
// set appearance to one NavigationController
let navVC = UINavigationController()
navVC.navigationBar.standardAppearance = appearance
navVC.navigationBar.scrollEdgeAppearance = appearance
// or you can config for all navigation bar
UINavigationBar.appearance().standardAppearance = appearance
UINavigationBar.appearance().scrollEdgeAppearance = appearance
back title base on you Viewcontroller
viewController.navigationItem.backButtonTitle = "your back title"

Swift 4.2
Add this functions ViewController
func addNavigationBarButton(imageName:String,direction:direction){
var image = UIImage(named: imageName)
image = image?.withRenderingMode(.alwaysOriginal)
switch direction {
case .left:
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: image, style:.plain, target: nil, action: #selector(goBack))
case .right:
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, style:.plain, target: nil, action: #selector(goBack))
}
}
#objc func goBack() {
navigationController?.popViewController(animated: true)
}
enum direction {
case right
case left
}
Using you should use here
viewDidLoad()
addNavigationBarButton(imageName: "ic_back", direction:.left)

Changing the navigation controller`s standardAppearance or scrollEdgeAppearance will reset your custom backIndicatorImage and backIndicatorTransitionMaskImage
let backImage = UIImage(systemName: "chevron.left.circle.fill")
navigationController?.navigationBar.backIndicatorImage = backImage
navigationController?.navigationBar.backIndicatorTransitionMaskImage = backImage
// this will cause the backIndicatorImage to be reset
let standardAppearance = UINavigationBarAppearance()
navigationController?.standardAppearance = standardAppearance
To change the backIndicatorImage in conjunction with UINavigationBarAppearance you will need to set the backImage using this:
navigationController?.standardAppearance.setBackIndicatorImage(backImage, transitionMaskImage: backImage)

You can change it globally in the AppDelegate with the following code:
UINavigationBar.appearance().backIndicatorImage = UIImage(named: "custom-back")
UINavigationBar.appearance().backIndicatorTransitionMaskImage = UIImage(named: "custom-back")

Related

Custom Back Indicator Image for Navigation Bar Swift

I am trying to set a custom image for the back bar button. I can remove the text, however, the default chevron arrow is still there. As illustrated below;
I'm on x-code 11.3
My code is;
let chevronImage = UIImage(systemName: "arrow.left")!.withAlignmentRectInsets(UIEdgeInsets(top: 0, left: -8, bottom: 0, right: 0))
navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
navigationController?.navigationBar.backIndicatorImage = chevronImage
navigationController?.navigationBar.backIndicatorTransitionMaskImage = chevronImage
You could try something like this:
navigationItem.leftBarButtonItem = UIBarButtonItem(image: UIImage(systemName: "arrow.left"),
style: .plain,
target: nil,
action: nil)
Try create your own custom navigation bar class and use that in viewDidLoad() function
let backButtonBackgroundImage = UIImage(named: "ic_navbar_back")!
let barAppearance = UINavigationBar.appearance(whenContainedInInstancesOf[CustomNavBar.self])
barAppearance.backIndicatorImage = backButtonBackgroundImage
barAppearance.backIndicatorTransitionMaskImage = backButtonBackgroundImage
Try this:
self.navigationController?.navigationBar.backIndicatorImage = images
self.navigationController?.navigationBar.backIndicatorTransitionMaskImage = images
self.navigationController?.navigationBar.tintColor = UIColor.clear

Navigation Bar stays while Pushing and Poping the View Controllers creating a weird White color Effect while transition

I have a navigationController which is being used to move between my ViewControllers.
I have it setup normally and I am using Xib's for each View Controller.
extension UINavigationController{
func setup(){
if #available(iOS 11.0, *) {
self.navigationController?.navigationBar.prefersLargeTitles = true
self.navigationController?.navigationItem.largeTitleDisplayMode = .always
} else {
// Fallback on earlier versions
}
self.navigationBar.isTranslucent = true
self.view.backgroundColor = UIColor.red
self.navigationBar.clipsToBounds = true
self.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationBar.shadowImage = UIImage()
self.navigationBar.tintColor = UIColor(hexString: "#373839")
}
}
This is the extension I am using for my UINavigation Controller.
And in the pushed View (Second View), I need to have different Left and right buttons so I am hiding the navigation backbitten and having a custom code to setup my buttons.
func setUI(){
self.navigationController?.navigationItem.hidesBackButton = true
self.navigationController?.navigationBar.clipsToBounds = true
self.navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
self.navigationController?.navigationBar.shadowImage = UIImage()
let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
let hostStr = host
print(hostStr)
let searchImage = UIImage(named: "Search_Icon3")!
let notificationImage = UIImage(named: "Notification_Icon3")!
let profileImage = UIImage(named: "test_Profile")!
let backImage = UIImage(named: "Back_Icon3")!
let searchButton = UIBarButtonItem(image: searchImage, style: .plain, target: self, action: #selector(searchViewButtonPressed(_:)))
let notificationButton = UIBarButtonItem(image: notificationImage, style: .plain, target: self, action: #selector(notificationViewButtonPressed(_:)))
let profileButton = UIBarButtonItem(image: profileImage, style: .plain, target: self, action: #selector(profileViewButtonPressed(_:)))
let backButton = UIBarButtonItem(image: backImage, style: .plain, target: self, action: #selector(backButtonPressed(_:)))
self.navigationItem.rightBarButtonItems = [profileButton,notificationButton,searchButton]
self.navigationController?.navigationBar.tintColor = UIColor(hexString: "#373839")
self.navigationItem.leftBarButtonItem = backButton
self.setValues()
}
But when I push from ViewController 1 to 2 and Pop from 2 to 1 I get this weird Nav bar present effect between transition

Custom UIBarButtonItem With BG Colour and Text Swift 3

I have two buttons on my right of my navigation bar.
extension UIViewController {
func setNavigationBarItem() {
let profileButton = UIBarButtonItem(title: "?", style: .plain, target: self, action: #selector(didTapProfileButton(_:)))
navigationItem.rightBarButtonItems = [addRightBarButtonWithImage(UIImage(named: "menu")!), profileButton]
self.slideMenuController()?.removeRightGestures()
self.slideMenuController()?.addRightGestures()
}
}
I have created 2 buttons like this. But the profileButton I want is with background colour and having corner radius to that button. How to add it to make it look like :
Ignore black part. UIBarButton will be of yellow colour background and corner radius.
For that you have only one option you need to create UIButton instance with design that you want after that create UIBarButtonItem instance from that button.
let btnProfile = UIButton(frame: CGRect(x: 0, y: 0, width: 25, height: 25))
btnProfile.setTitle("SY", for: .normal)
btnProfile.backgroundColor = UIColor.yellow
btnProfile.layer.cornerRadius = 4.0
btnProfile.layer.masksToBounds = true
Now create the UIBarButtonItem from that button and set it with the rightBarButtonItems.
navigationItem.rightBarButtonItems = [addRightBarButtonWithImage(UIImage(named: "menu")!), UIBarButtonItem(customView: btnProfile)]
let profileButton = UIButton()
profileButton.frame = CGRect(x:0, y:0, width:30, height:30)
profileButton.setTitle("SY", for: .normal)
profileButton.setTitle("SY", for: .highlighted)
profileButton.backgroundColor = UIColor.yellow
profileButton.layer.cornerRadius = 5.0
profileButton.addTarget(self, action: #selector(didTapProfileButton(_:)), for: .touchUpInside)
let rightBarButton = UIBarButtonItem(customView: profileButton)
self.navigationItem.rightBarButtonItem = rightBarButton

Cannot modify UIBarButton tintColor

I have a UIBarButton in my navigation bar, I set an image (silhouette.png) to it from the storyboard, and I can change that image's tint (color) at will:
if let num2 = Int(s, radix: 16) { //s="00ff00"
flamingoBtn.tintColor = UIColor(netHex:num2) //this btn is an IBoutlet
}
However, at some point I change the original image for another image (icon.png), programmatically, so I don't need to change the tint in this case, so far so good:
if let url = NSURL(string: "http://www.123di.com/CanonSGLens_132.png") {
if let data = NSData(contentsOfURL: url) {
var newImgThumb : UIImage=UIImage(data: data)!
var iconBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 35, height: 35))
iconBtn.setImage(newImgThumb, forState: UIControlState.Normal)
iconBtn.addTarget(self, action: "goToSettings:", forControlEvents: UIControlEvents.TouchUpInside)
var item = UIBarButtonItem(customView: iconBtn)
self.navigationItem.leftBarButtonItem = item
print("CUSTOM ICON: DOWNLOADED")
}
}
The problem comes, when I switch to the icon image to the original silhouette.png, because I cannot modify the tint any longer, it remains always blue silhoute (default color), instead of green, red, etc:
var newImgThumb : UIImage=UIImage(named: "happyface.png")!
var replyBtn = UIButton(frame: CGRect(x: 0, y: 0, width: 35, height: 35))
replyBtn.setImage(newImgThumb, forState: UIControlState.Normal)
replyBtn.addTarget(self, action: "goToSettings:", forControlEvents: UIControlEvents.TouchUpInside)
replyBtn.tintColor = UIColor.greenColor()
var item = UIBarButtonItem(customView: replyBtn)
item.tintColor = UIColor.greenColor()//UIColor(netHex:num2)
self.navigationItem.leftBarButtonItem = item
What am I doing wrong, why are tint changes igonred afterwards??
If you need extra details let me know.
If you google how to set the custom image for your bar button, pretty much everyone will tell you create a UIButton and then create the UIBarButtonItem from the UIButton as a customView:. In which case, you are doing the right thing here.
However, if you create the UIBarButtonItem via cutomView:, you are not able to change the tintColor any more. And this following is how you set custom image and also changing the tintColor
let item = UIBarButtonItem.init(image: UIImage(named: "happyface.png")!,
style: .Plain,
target: self,
action: Selector("goToSettings:"))
item.tintColor = UIColor.greenColor()
self.navigationItem.leftBarButtonItem = item
Depending where are you calling the code above, you might need to put it inside this structure, to make it run in the UI thread and make the change visible:
dispatch_async(dispatch_get_main_queue()) {
//code here
}
In case someone is still looking for the solution.
barButtonItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.red], for: .normal)
By setting the NSAttributedString.Key.foregroundColor of the title text attributes, you can set the color of the button.
If you follow this way you can do whatever you need.
let logoutNavBarButtonItem = UIBarButtonItem.init(
image: imageName,
style: .plain,
target: self,
action: #selector(buttonPressed))
navigationItem.leftBarButtonItem = logoutNavBarButtonItem

iOS rightBarButtonItem on UINavigationController in swift

I'm trying to put a rightBarButtonItem on a second view controller of an UINavigationViewController stack.
I'm creating and setting the button in viewDidLoad of the view controller that I want to show. My actual code looks like this:
override func viewDidLoad() {
super.viewDidLoad()
menu_button_ = UIBarButtonItem(image: UIImage(named: "menu"),
style: UIBarButtonItemStyle.Plain ,
target: self, action: "OnMenuClicked:")
self.navigationController!.navigationItem.rightBarButtonItem = menu_button_
}
What am I missing? The button doesn't appear.
You should set the menu_button_ as the rightBarButtonItem of your viewController rather than the navigationController.
Try
self.navigationItem.rightBarButtonItem = menu_button_
instead of
self.navigationController!.navigationItem.rightBarButtonItem = menu_button_
try with following code. it works for me.
let homeButton : UIBarButtonItem = UIBarButtonItem(title: "LeftButtonTitle", style: UIBarButtonItemStyle.Plain, target: self, action: "")
let logButton : UIBarButtonItem = UIBarButtonItem(title: "RigthButtonTitle", style: UIBarButtonItemStyle.Plain, target: self, action: "")
self.navigationItem.leftBarButtonItem = homeButton
self.navigationItem.rightBarButtonItem = logButton
And if you want to settle out custom image then please check with apple guidelines on below link.
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/BarIcons.html#//apple_ref/doc/uid/TP40006556-CH21-SW1
In Swift 5
//For righter button item
let rightBtn = UIBarButtonItem(image: UIImage(named: "rightmenu"), style: .plain, target: self, action: #selector(onClickMethod))//Change your function name and image name here
self.navigationItem.rightBarButtonItem = rightBtn
//self.navigationItem.rightBarButtonItem = [rightBtn, anotherBtn] //If you want to add more buttons add like this
//This is your function
#objc func onClickMethod() {
print("Left bar button item")
}
Create an extension of UINavigationItem like -
extension UINavigationItem {
func addSettingButtonOnRight(){
let button = UIButton(type: .custom)
button.setTitle("setting", for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 15.0)
button.layer.cornerRadius = 5
button.backgroundColor = .gray
button.frame = CGRect(x: 0, y: 0, width: 100, height: 25)
button.addTarget(self, action: #selector(gotSettingPage), for: UIControlEvents.touchUpInside)
let barButton = UIBarButtonItem(customView: button)
self.rightBarButtonItem = barButton
}
#objc func gotSettingPage(){
}
}
And call it from viewDidLoad() like -
self.navigationItem.addSettingButtonOnRight()

Resources