Adding multiple custom bar buttons to custom nav bar - ios

I need to add two bar button items to each end of my custom navigation bar in Swift. I'm using the following method, and although I get no errors, nothing at all is appearing. I'm using my own custom icons, which do appear when I add them using interface builder. Obviously, I can only add one to each end that way.
#IBOutlet weak var navBar: UINavigationBar!
override func viewDidLoad() {
var iconOne = UIImage(named: "iconOne")
var iconTwo = UIImage(named: "iconTwo")
var buttonOne:UIBarButtonItem = UIBarButtonItem(image: iconOne, style: UIBarButtonItemStyle.Plain, target: self, action: nil)
var buttonTwo:UIBarButtonItem = UIBarButtonItem(image: iconTwo, style: UIBarButtonItemStyle.Plain, target: self, action: nil)
self.navBar.setItems([buttonOne,buttonTwo], animated: true)
}
This is implemented in a view controller that's embedded in a navigation controller. I'd be able to use self.navigationItem.setRightBarButtonItems([buttonOne, buttonTwo], animated: true) if I weren't using a custom nav bar. What's the workaround?

This code works for me:
#IBOutlet weak var navBar: UINavigationBar!
#IBOutlet weak var navBarItem: UINavigationItem!
func displayTextInNavBar(text: String) {
let labelWidth: CGFloat = navBar.frame.width / 6
let frame = CGRect(x: 0, y: 0, width: labelWidth, height: navBar.frame.height)
let label = UILabel(frame: frame)
label.textAlignment = .Right
label.textColor = UIColor(red: 0/255, green: 127/255, blue: 0/255, alpha: 1)
label.font = UIFont(name: "Bradley Hand", size: 20)
label.text = text
let navBarButtonItem = UIBarButtonItem(customView: label)
navBarItem.rightBarButtonItem = navBarButtonItem
}
The above puts a label on the right side of the navBar. If you want an actual button that is clickable, do this instead:
let navBarButtonItem = UIBarButtonItem(title: text, style: .Plain, target: self, action: nil)
If you want that button to actually do something when clicked, then instead of nil specify some function.

My solution to do it:
var barButton : UIBarButtonItem?
override func viewDidLoad() {
self.barButton = UIBarButtonItem(title: "Options", style: .plain, target: self, action: nil))
self.navigationItem.rightBarButtonItems = [barButton] as? [UIBarButtonItem]
}

UIBarButtonItem can be created with an custom view
create a view
add UIButtons to it
layout it
create a UIBarButtonItem with custom view
add it to the navigation bar

Related

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: How do I create a custom UINavigationBar and add a custom back button?

I am trying to create a custom navigationBar.
I am hiding the original navigationBar in viewWillAppear like so:
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = true
}
I am subclassing UINavigationBar like so:
let navBar: UINavigationBar = {
let view = UINavigationBar()
view.backgroundColor = .clear
view.isTranslucent = true
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
In viewDidLoad I am calling setupNavBar():
func setupNavBar() {
view.addSubview(navBar)
self.navBar.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 80)
let backButton = UIBarButtonItem(image: UIImage(named:"backThick"), style: .plain, target: self, action: #selector(popControllerOffStack))
}
The problem is that backButton is added to the original navigationBar that is being hidden. This makes me think I am incorrectly creating the navigationBar. How do I add the button to navBar?
Updated Code (still not working):
class CustomNavBar: UINavigationBar {
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = .red
}
}
// In the viewController
let navBar = CustomNavBar()
override func viewDidLoad() {
super.viewDidLoad()
setupNavBar()
}
func setupNavBar() {
view.addSubview(navBar)
navBar.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 80)
let backButton = UIBarButtonItem(image: UIImage(named:"backThick"), style: .plain, target: self, action: #selector(popControllerOffStack))
self.navigationItem.leftBarButtonItem = backButton
}
The button is being added to the original navigationBar.
You are not subclassing UINavigationBar. Rather, you are creating a new instance of UINavigationBar and modifying its properties inside a computed variable. This means each time you access navBar, you are initializing a new UINavigationBar object.
To create a subclass:
class MyCustomNavigationBar: UINavigationBar {
// Set properties in here after initialization
}
Once you have a proper subclass created, You can initialize an instance like so:
var navBar = MyCustomNavigationBar()
Finally, add your button to the navigation bar:
let backButton = UIBarButtonItem(image: UIImage(named:"backThick"), style: .plain, target: self, action: #selector(popControllerOffStack))
// Assuming 'self' is an instance of UINavigationController()
self.navigationItem.leftBarButtonItem = backButton
See the official Swift Programming Language Guide on Inheritance.
Swift 3.0
You can set custom Back button as like below
self.navigationItem.hidesBackButton = true
let backButton = UIBarButtonItem(image: UIImage(named: "image_name"), style: .plain, target: self, action: #selector(Class.methodName))
backButton.tintColor = UIColor.white
self.navigationItem.leftBarButtonItem = backButton
Also you can try below code:
let btnLeftMenu: UIButton = UIButton()
btnLeftMenu.setImage(UIImage(named: "image_name"), for:UIControlState())
btnLeftMenu.addTarget(self, action: #selector(moveImage), for:UIControlEvents.touchUpInside)
btnLeftMenu.frame = CGRect(x: 0, y: 0, width: 25, height: 25)
let barButton = UIBarButtonItem(customView: btnLeftMenu)
self.navigationItem.leftBarButtonItem = barButton*

BarButtonItem EdgeInsets doesn't work

I'm trying to add a padding to the right of a navigationBar rightBarButtonItems in between the buttons but it's now working.
Here's the button creation code with the given inset:
lazy var previewBarButtonItem: UIBarButtonItem = {
let buttonItem = UIBarButtonItem(title: "Preview", style: .plain, target: self, action: #selector(handlePreview))
buttonItem.tintColor = UIColor.black
buttonItem.setTitleTextAttributes([NSFontAttributeName: UIFont.systemFont(ofSize: 14)], for: .normal)
buttonItem.imageInsets = UIEdgeInsetsMake(0, -15, 0, 15)
return buttonItem
}()
override func viewDidLoad() {
super.viewDidLoad()
setupRightNavItems()
}
fileprivate func setupRightNavItems () {
navigationItem.rightBarButtonItems = [submitBarButtonItem, previewBarButtonItem]
}
Can you give a hint on why this isn't working?
Thanks
I managed to fix this issue, just added a fixedSpace in-between the bar button items as follows:
1) remove any EdgeInsets
2) add the following fixed space:
fileprivate func setupRightNavItems () {
let spacing = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
spacing.width = 25 //change to your desired space width
navigationItem.rightBarButtonItems = [submitBarButtonItem, spacing, previewBarButtonItem]
}

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

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