Custom Back Indicator Image for Navigation Bar Swift - ios

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

Related

UIBarButtonItem with SF Symbols displays smaller than SystemItem in the navigation bar of document browser

In my view controller that extends UIDocumentBrowserViewController, I did this
additionalTrailingNavigationBarButtonItems = [
UIBarButtonItem(
image: UIImage(systemName: "book"),
style: .plain,
target: self,
action: nil
),
UIBarButtonItem(
barButtonSystemItem: .bookmarks,
target: self,
action: #selector(onReaderButtonClicked)
),
]
and results in
But if I do similar things in another view controller that is shown by UINavigationController
navigationItem.rightBarButtonItems = [
UIBarButtonItem(
image: UIImage(systemName: "book"),
style: .plain,
target: nil,
action: nil
),
UIBarButtonItem(
barButtonSystemItem: .bookmarks,
target: nil,
action: nil
),
]
The two icon have the same size as I expected
Why is the result in pic 1 happened and how can i deal with it?
Try to add a custom button in your navigation bar, add bar button items array in right or left position like this:
let customButtonWithImage = UIButton(type: .system) // declare your button
now in view did load set image, title and assign array of buttons to your navigation bar:
let image = UIImage(systemName: "book")?.withRenderingMode(.alwaysTemplate)
customButtonWithImage.setImage(image, for: .normal)
customButtonWithImage.setTitle(" Select", for: .normal)
customButtonWithImage.titleLabel?.font = .systemFont(ofSize: 16, weight: .regular)
customButtonWithImage.setTitleColor(.white, for: .normal) // set text color
let customNavBarButton = UIBarButtonItem(customView: customButtonWithImage)
let navBarButton = UIBarButtonItem(image: UIImage(systemName: "book"), style: .plain, target: self, action: nil)
navigationItem.rightBarButtonItems = [customNavBarButton, navBarButton]
navigationController?.navigationBar.tintColor = .white // set tint color
This is the result:
I couldn't reproduce your issue, but be aware you're allowed to specify an UIImage.SymbolConfiguration object to be used, when creating an UIImage with the systemName: method.
The scale used on navigation items is .large. So if you create the image like this:
let config = UIImage.SymbolConfiguration(scale: .large)
let image = UIImage(systemName: "book", withConfiguration: config)
let button = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(ItemsTableViewController.settingsTapped))
self.navigationItem.leftBarButtonItem = button
It will create an image that matches the size of a UIBarButtonItem created using barButtonSystemItem:.

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

Why are programmatically created (bottom) toolbar button titles not displaying?

In Swift 3, I've programmatically created a (bottom) toolbar with custom buttons separated by a flexible spacer to separate the custom buttons, pushing one ("Previous") to the left edge and the other ("Next") to the right edge of the view. But I have not been able to get the button titles ("Previous" and "Next") to display. Please advise me. Thanks. Here is my code:
First, the class declaration and a few variables:
class TextsController: UIViewController,UIGestureRecognizerDelegate {
let textWebView = WKWebView(frame: .zero)
var toolbar:UIToolbar?
Next, in the viewDidLoad:
view = textWebView
// Create (bottom) toolbar:
let frame = CGRect(x: 0, y: UIScreen.main.bounds.size.height-44, width: UIScreen.main.bounds.size.width, height: 44)
toolbar = UIToolbar(frame: frame)
toolbar?.sizeToFit()
toolbar?.isHidden = true
self.view.addSubview(toolbar!)
self.toolbar?.isTranslucent = false // Required for setting tintColor & barTintColor below
toolbar?.tintColor = UIColor(red: 0.5, green: 0.0, blue: 1.0, alpha: 1.0) //purple for toolbar items
toolbar?.barTintColor = UIColor.white //white for toolbar color
let triangleLeftButton = UIBarButtonItem(image: UIImage(named: "triangleLeft_15x20"), style: .plain, target: self, action:#selector(showPrevious))
let flexibleSpacer = UIBarButtonItem(barButtonSystemItem:.flexibleSpace , target: self, action: nil)
let triangleRightButton = UIBarButtonItem(image: UIImage(named: "triangleRight_15x20"), style: .plain, target: self, action:#selector(showNext))
triangleLeftButton.title = "Previous"
triangleRightButton.title = "Next"
var items = [UIBarButtonItem]()
items.append(triangleLeftButton)
items.append(flexibleSpacer)
items.append(triangleRightButton)
toolbar?.items = items
A custom view or image bar button item has no title. A bar button item is either a title or a custom view or an image. If you want some words to appear in a custom view or image bar button item, make those words part of the custom view or image.
Here is how I solved this.
I eliminated the two statements attempting to assign a title to each button (I do wonder why Xcode did not flag that, allowing me to assign a title to each initially, though neither title would display).
I rewrote (compare the code below with the code in the question) the UIBarButtonItems as follows to now include two text items:
// Create toolbar items (buttons and texts)
let triangleLeftButton = UIBarButtonItem(image: UIImage(named: "triangleLeft_15x20"), style: .plain, target: self, action:#selector(showPrevious))
let triangleLeftText = UIBarButtonItem(title: "Previous", style: .plain, target: nil, action: nil)
let flexibleSpacer = UIBarButtonItem(barButtonSystemItem:.flexibleSpace , target: self, action: nil)
let triangleRightText = UIBarButtonItem(title: "Next", style: .plain, target: nil, action: nil)
let triangleRightButton = UIBarButtonItem(image: UIImage(named: "triangleRight_15x20"), style: .plain, target: self, action:#selector(showNext))
Then I appended everything, including the two new text items, to the array:
var items = [UIBarButtonItem]()
items.append(triangleLeftButton)
items.append(triangleLeftText)
items.append(flexibleSpacer)
items.append(triangleRightText)
items.append(triangleRightButton)
toolbar?.items = items
This effectively places "Previous" adjacent to the left-pointing button and "Next" adjacent to the right-pointing button, with the flexible spacer affording a nice wide gap between.

Swift Custom NavBar Back Button Image and Text

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")

Navigation bar button image

I am using this code to get a logo on my nav bar.
override func viewDidAppear(animated: Bool) {
let image = UIImage(named: "LogoWithTextSmaller.png")
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: image, style: UIBarButtonItemStyle.Plain, target: nil, action: nil)
}
This is fine, but the logo doesn't have any colour - besides 'blue'. Is it because it is a png file. Is there something I can so it retains the original colours
I have done this:
self.navigationItem.titleView = UIImageView(image: image)
and that brings the image onto the nav bar with the correct colours - but it's in the middle and I want it on the left.
You need to declare that the image stays original all the time. so add the code as below
var image = UIImage(named: "image-name")
image = image?.withRenderingMode(.alwaysOriginal)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: image, style:.plain, target: nil, action: nil)
In Swift 3 the same would be accomplished using the following syntax
var image = UIImage(named: "Filter")
image = image?.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image:image , style: UIBarButtonItemStyle.plain, target: nil, action: nil)
Swift 3.0
let btnLogo = UIButton(frame: CGRect(x: 0, y: 0, width: 25, height: 25))
btnLogo.setTitle("", for: .normal)
btnLogo.backgroundColor = UIColor.clear
btnLogo.layer.cornerRadius = 4.0
btnLogo.layer.masksToBounds = true
var imageLogo = UIImage(named: "LogoWithTextSmaller.png")
imageLogo = imageLogo?.withRenderingMode(UIImageRenderingMode.alwaysOriginal)
btnLogo.setImage(imageLogo, for: .normal)
let barButton = UIBarButtonItem(customView: btnLogo)
self.navigationItem.leftBarButtonItem = barButton
swift 2.0
var image = UIImage(named: "Filter")
image = image?.imageWithRenderingMode(UIImageRenderingMode.alwaysOriginal)
Objective-C
UIImage *image = [[UIImage alloc] imageWithRenderingMode:UIImageRenderingModeAlwaysOriginal];
UIBarButtonItem *_btnLeftBar = [[UIBarButtonItem alloc]initWithImage:[UIImage imageNamed:#"LogoWithTextSmaller.png"]
style:UIBarButtonItemStylePlain
target:self
action:#selector(yourMethod)];
self.navigationItem.rightBarButtonItem= _btnLeftBar;
For withRenderingMode(_:) details see below apple documentation link
https://developer.apple.com/documentation/uikit/uiimage/1624153-withrenderingmode
SWIFT 4
let back = UIImage(named: "back_white")?.withRenderingMode(.alwaysOriginal)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: back, style:.plain, target: nil, action: nil)
In swift 3.0
let Navigateimage = UIImage(named: "LogoWithTextSmaller.png")
Navigateimage = Navigateimage?.withRenderingMode(.alwaysOriginal)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(image: Navigateimage, style:.plain, target: nil, action: nil)
Swift 3
If the item is missing, you can try this.
let navigationBar = navigationController?.navigationBar
let topItem = navigationBar?.topItem
var navigateimage = UIImage(named: "addConnectionFromSupport")
navigateimage = navigateimage?.withRenderingMode(.alwaysOriginal)
topItem?.rightBarButtonItem = UIBarButtonItem(image: navigateimage, style:.plain, target: nil, action: nil)

Resources