Swift: navigation bar item text and image - ios

I use this code to show image and text in navigation bar item.
func barItem() {
let button = UIButton(type: .system)
button.setImage(UIImage(named: "trashButtonItem.png"), for: .normal)
button.setTitle("Читать", for: .normal)
button.sizeToFit()
self.navigationItem.setLeftBarButton(UIBarButtonItem(customView: button), animated: true);
}
In this case I have image on the left and text on the right. But I want to have image on the right and text on the left. How to do it?

You can change the button semanticContentAttribute
func barItem() {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "trashButtonItem.png"), for: .normal)
button.setTitle("Читать", for: .normal)
button.semanticContentAttribute = .forceLeftToRight
button.sizeToFit()
self.navigationItem.setLeftBarButton(UIBarButtonItem(customView: button), animated: true);
}
You can also handle RTL
button.semanticContentAttribute = UIApplication.shared
.userInterfaceLayoutDirection == .rightToLeft ? .forceLeftToRight : .forceRightToLeft

You need to adjust the titleEdgeInsets and the imageEdgeInsets of your UIButton.
Before:
After:

Related

UIButton title and image positions are swapped unexpectedly on clicked

I'd like to make a button that the image is on the right instead of the left side.
This is the expected layout:
I'm able to satisfy the layouts with the following code:
import UIKit
class ViewController: UIViewController {
private lazy var leftButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(didTap), for: .touchUpInside)
button.setImage(UIImage(systemName: "trash"), for: .normal)
button.setTitle("Trash", for: .normal)
button.setTitleColor(.systemBlue, for: .normal)
if let imageView = button.imageView {
imageView.contentMode = .scaleAspectFit
NSLayoutConstraint.activate([
imageView.widthAnchor.constraint(equalToConstant: 8),
imageView.heightAnchor.constraint(equalToConstant: 8)
])
}
button.contentHorizontalAlignment = .left
button.contentVerticalAlignment = .center
button.semanticContentAttribute = .forceRightToLeft
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: leftButton)
}
#objc private func didTap() {
}
}
Everything looked exactly what I wanted at the beginning until I clicked the button. The positions of the title and the image were swapped. Please take a look at the gif:
Does anyone have any clue on why this happened? And how can I improve my code?
I guess it may have something to do with the navigation item.
Try it like this:
let button = UIButton(type: .system)
button.setTitle("Trash", for: .normal)
button.setImage(UIImage(systemName: "trash"), for: .normal)
button.setPreferredSymbolConfiguration(
.init(scale: .small), forImageIn: .normal
)
button.widthAnchor.constraint(equalToConstant: 60).isActive = true
button.imageEdgeInsets.right = -75
button.titleEdgeInsets.left = -35
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button)
However, if you can confine yourself to iOS 15 and later, this is a lot more simple and coherent, because the notion of a trailing image is built right in:
var config = UIButton.Configuration.plain()
config.title = "Trash"
config.image = UIImage(systemName: "trash")?
.applyingSymbolConfiguration(.init(scale: .small))
config.imagePlacement = .trailing
let button = UIButton(configuration: config)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button)

Custom button as leftBarButtonItem

I'm using this code to display a custom button as leftButtonItem:
let button = UIButton(type: .system)
button.setTitleColor(.white, for: .normal)
button.setImage(UIImage(named: "Back"), for: UIControlState.normal)
button.addTarget(self, action: #selector (DetailExperienceTVC.back), for: .touchUpInside)
button.setTitle(DataManager.shared.arrayMenuTop[DataManager.shared.indexTitle].title, for: .normal)
button.titleLabel?.font = ColorManager.shared.generalFont30
button.sizeToFit()
button.titleEdgeInsets = UIEdgeInsetsMake((self.navigationController!.navigationBar.frame.height) / 4, 10, 0, 0)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: button)
but this is what I get wrong size button, my question is how can I distanziate the text from the image without cropping it?
Thanks in advance!
Here's the fix:
button.titleLabel?.adjustsFontSizeToFitWidth = true

Change UIButton textlabel color by touching the button Swift 3

I read some articles but didn't find a solution that solved my problem. Currently im building an iOS app in Swift 3. I placed a UIButton called registerButton into my view. If someone touches the button a new ViewController shall appear(newController). Beside that the color of the registerButton.textlabel.setColor() should be changed when a user touches the button and hold it pressed ? The presentation of the newController works but not the change color of the textLabel. Im changing the color of the registerButton in the Registration function. Has someone an idea ? Thanks
var registerButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Registration", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(registration), for: .touchUpInside) // Action
return button
}()
func registration(){
let color = UIColor(r: 255, g: 0, b: 102)// red
registerButton.setTitleColor(color, for: .highlighted)
let newController = ViewController()
present(newController, animated: true, completion: nil)
}
Button normal
Button pressed no red text label
var registerButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Registration", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(colorChange), for: .touchDown)
button.setTitleColor(.red, for: .highlighted)
// Action
return button
}()
This should work just fine
Try to change the color for the state Focused when you are creating the button, not when pressed.
var registerButton: UIButton = {
let button = UIButton(type: .system)
button.setTitle("Registration", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.setTitleColor(UIColor.red, for: .focused)
button.addTarget(self, action: #selector(registration), for: .touchUpInside) // Action
return button
}()

Change color of disabled bar button item in iOS

I need to display my app's icon in the navigation bar. To do this, I have added it as a right bar button item. I don't want it to be clickable, I just need the icon there, so I set it to disabled. The problem with this is the icon appears grey, instead of green. Is there a way to disable this button but also keep it's original color?
Try this:
let barButtonItem = UIBarButtonItem(title: "Click", style: .Done, target: self, action: #selector(didClick(_:)))
barButtonItem.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.blueColor()], forState: .Normal)
barButtonItem.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.blueColor()], forState: .Disabled)
barButtonItem.enabled = false
navigationItem.setRightBarButtonItem(barButtonItem, animated: false)
try
[button setBackgroundImage:yourIconImage forState:UIControlStateDisabled];
[button setEnabled:NO]
The accepted answer does not work for me (I think it's because I'm using an image, not text).
You can initialize a UIBarButtonItem using a custom view, so in order to solve the issue I subclassed UIBarButtonItem so that it takes a UIButton as an initializer argument.
Here is the code.
class TintableBarButtonItem: UIBarButtonItem {
private(set) var button: UIButton!
override var tintColor: UIColor? {
get { return button.tintColor }
set { button.tintColor = newValue }
}
convenience init(button: UIButton) {
self.init(customView: button)
self.button = button
button.imageView?.contentMode = .scaleAspectFit
button.frame = CGRect(x: 0, y: 0, width: 34, height: 30)
}
}
And I used it like this in my ViewController:
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton()
let image = #imageLiteral(resourceName: "MyIcon").withRenderingMode(.alwaysTemplate)
button.setImage(image, for: .normal)
let barButton = TintableBarButtonItem(button: button)
navigationItem.rightBarButtonItem = barButton
}
I got the frame dimensions using the method here:
https://stackoverflow.com/a/45374012/6167296
You will also have to set the target-actions on the button itself.
You can see a similar answer here: https://stackoverflow.com/a/2796488/6167296
Swift 5:
leftDrawerButton.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: .normal)
leftDrawerButton.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.black], for: .disabled)
myBarButtonItem.setBackgroundImage(myIconImage, forState: .Normal, barMetrics: .Default)
myBarButtonItem.setBackgroundImage(myIconImage, forState: .Disabled, barMetrics: .Default)
myBarButtonItem.enabled = false
To set the color for disabled state across the entire app, you can do the following once in your AppDelegate's application:didFinishLaunchingWithOptions::
let attributes: [String: Any] = [
NSForegroundColorAttributeName: UIColor.red.withAlphaComponent(0.5)
]
UIBarButtonItem.barAppearanceWhenContained(in: UINavigationBar.self)
.setTitleTextAttributes(attributes, for: UIControlState.disabled)
For swift 4.X
let barButtonItem = UIBarButtonItem(title: "Click", style: .done, target: self, action: #selector(didClick(_:)))
barButtonItem.setTitleTextAttributes([NSAttributedStringKey.foregroundColor : UIColor.blueColor()], forState: .normal)
barButtonItem.setTitleTextAttributes([NSAttributedStringKey.foregroundColor : UIColor.blueColor()], forState: .disabled)
barButtonItem.enabled = false
navigationItem.setRightBarButtonItem(barButtonItem, animated: false)
basically NSForegroundColorAttributeName became NSAttributedStringKey.foregroundColor and states went from being caps to lower case .Disabled became .disabled , etc..
Swift 5:
let barButtonItem = UIBarButtonItem(title: "Click", style: .done, target: self, action: #selector(didClick(_:)))
barButtonItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.blue], for: .normal)
barButtonItem.setTitleTextAttributes([NSAttributedString.Key.foregroundColor: UIColor.blue], for: .disabled)
barButtonItem.isEnabled = false
navigationItem.setRightBarButton(barButtonItem, animated: false)

How to programmatically create a "Back" UIBarButton item in Swift?

I was able to create a UIBarButton item that can go back programmatically using the following code:
func backAction() -> Void {
self.navigationController?.popViewControllerAnimated(true)
}
override func viewDidLoad() {
super.viewDidLoad()
let backButton = UIBarButtonItem(title: "Back", style: UIBarButtonItemStyle.Plain, target: self, action: "backAction")
self.navigationItem.leftBarButtonItem = backButton
}
The problem is that the back button doesn't have the left pointing arrow:
Is there a way to make it look like a regular back button with the arrow like this:
I would also like to know if there is a way to make the button title names as the title of the previous view controller, if that's possible.
Thanks
Below is the code by using UIButton with image you can add it as a customView for UIBarButtonItem
override func viewDidLoad() {
super.viewDidLoad()
var backbutton = UIButton(type: .Custom)
backbutton.setImage(UIImage(named: "BackButton.png"), forState: .Normal) // Image can be downloaded from here below link
backbutton.setTitle("Back", forState: .Normal)
backbutton.setTitleColor(backbutton.tintColor, forState: .Normal) // You can change the TitleColor
backbutton.addTarget(self, action: "backAction", forControlEvents: .TouchUpInside)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backbutton)
}
func backAction() -> Void {
self.navigationController?.popViewControllerAnimated(true)
}
Download Link
For setting the title of backbutton with the previous view controller title you have to pass the Title as a String while presenting the controller make change to above code as
var titleStrFromPreviousController: String // This value has to be set from previous controller while presenting modal controller
backbutton.setTitle(titleStrFromPreviousController, forState: .Normal)
This may help.
Swift 3
override func viewDidLoad() {
super.viewDidLoad()
addBackButton()
}
func addBackButton() {
let backButton = UIButton(type: .custom)
backButton.setImage(UIImage(named: "BackButton.png"), for: .normal) // Image can be downloaded from here below link
backButton.setTitle("Back", for: .normal)
backButton.setTitleColor(backButton.tintColor, for: .normal) // You can change the TitleColor
backButton.addTarget(self, action: #selector(self.backAction(_:)), for: .touchUpInside)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
}
#IBAction func backAction(_ sender: UIButton) {
let _ = self.navigationController?.popViewController(animated: true)
}
Updated for Swift 4.2 - thanks to sam bing and silentbeep
Made some modifications on some colors and action's selector.
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .blue
self.navigationItem.title = title
self.navigationController?.navigationBar.barTintColor = .white
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: makeBackButton())
}
func makeBackButton() -> UIButton {
let backButtonImage = UIImage(named: "backbutton")?.withRenderingMode(.alwaysTemplate)
let backButton = UIButton(type: .custom)
backButton.setImage(backButtonImage, for: .normal)
backButton.tintColor = .blue
backButton.setTitle(" Back", for: .normal)
backButton.setTitleColor(.blue, for: .normal)
backButton.addTarget(self, action: #selector(self.backButtonPressed), for: .touchUpInside)
return backButton
}
#objc func backButtonPressed() {
dismiss(animated: true, completion: nil)
// navigationController?.popViewController(animated: true)
}
You can do by embedding your view in a navigation controller.
Here is an image showing how to do that:
Hope it helps :D
For future searches, I wanted to add that you can now use the default icon by this code:
override public func viewDidLoad() {
// create chevron image
let config = UIImage.SymbolConfiguration(pointSize: 25.0, weight: .medium, scale: .medium)
let image = UIImage(systemName: "chevron.left", withConfiguration: config)
// create back button
let backButton = UIButton(type: .custom)
backButton.addTarget(self, action: #selector(self.close(_:)), for: .touchUpInside)
backButton.setImage(image, for: .normal)
backButton.setTitle("Back", for: .normal)
backButton.setTitleColor(backButton.tintColor, for: .normal)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
}
#IBAction func close(_ sender: UIButton) {
self.navigationController?.dismiss(animated: true, completion: nil)
}
The first answer works great however the image is a bit too big so use the preview and scale it down to width:13 and height: 22, also set its rendering mode to .alwaysTemplate and change the UIButton's tint to white, while also adding two spaces before the string : " Back". This will result in something that is quiet similar to the navigation bar back button, the image could be better in terms of size and placement.
Edited code:
func addBackButton() {
let backButtonImage = UIImage(named: "BackButton.png")?.withRenderingMode(.alwaysTemplate)
let backButton = UIButton(type: .custom)
backButton.setImage(backButtonImage, for: .normal)
backButton.tintColor = .white
backButton.setTitle(" Back", for: .normal)
backButton.setTitleColor(.white, for: .normal)
backButton.addTarget(self, action: #selector(self.backAction(_:)), for: .touchUpInside)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
}
I changed one last line of code from selected answer and it works for me.
override func viewDidLoad() {
super.viewDidLoad()
addBackButton()
}
func addBackButton() {
let backButton = UIButton(type: .custom)
backButton.setImage(UIImage(named: "BackButton.png"), for: .normal) // Image can be downloaded from here below link
backButton.setTitle("Back", for: .normal)
backButton.setTitleColor(backButton.tintColor, for: .normal) // You can change the TitleColor
backButton.addTarget(self, action: #selector(self.backAction(_:)), for: .touchUpInside)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backButton)
}
#IBAction func backAction(_ sender: UIButton) {
let _ = self.dismiss(animated: true, completion: nil)
}
Swift 5
override func viewDidLoad() {
super.viewDidLoad()
let backbutton = UIButton(type: .custom)
backbutton.setImage(UIImage(named: "BackButton.png"), for: .normal) // Image can be downloaded from here below link
backbutton.setTitle("Back", for: .normal)
backbutton.setTitleColor(backbutton.tintColor, for: .normal) // You can change the TitleColor
backbutton.addTarget(self, action: Selector(("backAction")), for: .touchUpInside)
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: backbutton)
}
func backAction() -> Void {
self.navigationController?.popViewController(animated: true)
}

Resources