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

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

Related

Swift - Toggle UIButton title when selected

I'm looking to implement a button that can be used as a tickbox and enablers the user to toggle the tickbox in an on/off fashion (unticked/ticked). Currently I've set up my button using the attributes inspector including the 'Title' as "X" and the 'Text Color' is red.
Once loaded the button appears a red "X", once tapped it turns to a green tick.
My question is... how do you enable the button to be tapped again to revert back to the red X (it's original state), an continue in a loop whenever tapped?
#IBAction func check2(_ sender: UIButton) {
sender.setTitle("✓", for: .normal)
sender.setTitleColor(UIColor.green, for: UIControlState.normal)
}
Thank you
Track the state with a variable and update the appearance based on the state:
class ViewController: UIViewController{
#IBOutlet weak var button: UIButton!
var isChecked = true
#IBAction func check2(_ sender: UIButton) {
isChecked = !isChecked
if isChecked {
sender.setTitle("✓", for: .normal)
sender.setTitleColor(.green, for: .normal)
} else {
sender.setTitle("X", for: .normal)
sender.setTitleColor(.red, for: .normal)
}
}
}
Updated for Swift 3
lazy var toggleBT: UIButton = {
let button = UIButton()
button.frame = CGRect(x: 40, y: 100, width: 200, height: 40)
button.backgroundColor = .orange
button.isSelected = false // optional(because by default sender.isSelected is false)
button.setTitle("OFF", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = .boldSystemFont(ofSize: 14)
button.addTarget(self, action: #selector(handleToggleBT), for: .touchUpInside)
return button
}()
func handleToggleBT(sender: UIButton) {
sender.isSelected = !sender.isSelected
if sender.isSelected {
print(sender.isSelected)
toggleBT.setTitle("ON", for: .normal)
}
else {
print(sender.isSelected)
toggleBT.setTitle("OFF", for: .normal)
}
} // don't forget to add this button as a subView for eg. view.addSubview(toggleBT)
Updated code for swift5
#IBAction func check2(_ sender: UIButton) {
sender.isSelected = !sender.isSelected //By default sender.isSelected is false
if sender.isSelected {
sender.setTitle("✓", for: .normal)
sender.setTitleColor(UIColor.green, for: .normal)
} else {
sender.setTitle("x", for: .normal)
sender.setTitleColor(UIColor.red, for: .normal)
}
}

Set image and title for bar button item?

I currently have a custom navigation controller with bar button items that are simply text buttons. Is it possible to keep the title of the bar button items but also set them as images (icon image + title underneath).
class NavigationController: UINavigationController
{
var mode: NavigationMode = .Swipe {
didSet {
self.setButtonAttributes()
}
}
private var leftBarButton: UIBarButtonItem!
private var middleBarButton: UIBarButtonItem!
private var rightBarButton: UIBarButtonItem!
private var rightBarButton2: UIBarButtonItem!
override func viewDidLoad()
{
super.viewDidLoad()
}
func configureNavigationItem(navigationItem: UINavigationItem)
{
//Configure the bar buttons text and actions
if (self.leftBarButton == nil) {
self.leftBarButton = UIBarButtonItem(title: "Menu1", style: .Plain,target: self, action: "menu1Pressed:")
}
if (self.middleBarButton == nil) {
self.middleBarButton = UIBarButtonItem(title: "Games", style: .Plain, target: self, action: "gamesPressed:")
}
if (self.rightBarButton == nil) {
self.rightBarButton = UIBarButtonItem(title: "Menu3", style: .Plain, target: self, action: "menu3Pressed:")
}
if (self.rightBarButton2 == nil) {
self.rightBarButton2 = UIBarButtonItem(title: "Settings", style: .Plain, target: self, action: "settingsPressed:")
}
self.setButtonAttributes()
navigationItem.leftBarButtonItems = [self.leftBarButton, self.middleBarButton, self.rightBarButton, self.rightBarButton2]
}
Updated:
let button = UIButton(type: .System)
button.setImage(UIImage(named: "play"), forState: .Normal)
button.setTitle("Play", forState: .Normal)
button.sizeToFit()
leftBarButton = UIBarButtonItem(customView: button)
if (self.leftBarButton == nil) {
self.leftBarButton = UIBarButtonItem(title: "Play", style: .Plain,target: self, action: "Pressed:")
}
You can create UIButton instance, set an image and a title for it, and then create your UIBarButtonItem with it:
let button = UIButton(type: .System)
button.setImage(UIImage(named: "YourImage"), forState: .Normal)
button.setTitle("YourTitle", forState: .Normal)
button.sizeToFit()
self.leftBarButton = UIBarButtonItem(customView: button)
To add an action:
button.addTarget(self, action: #selector(self.someAction), forControlEvents: .TouchUpInside)
where self.someAction is
func someAction() {
}
Create an UIButton, set an image and a title for it and use it as a custom image to init your UIBarButtonItem(customView:) with it.
If you want the image to be on the right side of the button, you can set the button's semanticContentAttribute to .forceRightToLeft.
Swift 4 example:
let view = UIView()
let button = UIButton(type: .system)
button.semanticContentAttribute = .forceRightToLeft
button.setImage(UIImage(named: "DocumentsIcon"), for: .normal)
button.setTitle("Documents", for: .normal)
button.addTarget(self, action: #selector(openDocuments), for: .touchUpInside)
button.sizeToFit()
view.addSubview(button)
view.frame = button.bounds
navigationItem.rightBarButtonItem = UIBarButtonItem(customView: view)
Swift 3:
let button = UIButton(type: .system)
button.setImage(UIImage(named: "categories_icon"), for: .normal)
button.setTitle("Categories", for: .normal)
button.addTarget(self, action: #selector(showCategories), for: .touchUpInside)
button.sizeToFit()
self.navigationItem.leftBarButtonItem = UIBarButtonItem(customView: 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)

Swift add show action to button programmatically

how can I add action to button programmatically. I need to add show action to buttons in mapView. thanks
let button = UIButton(type: UIButtonType.Custom) as UIButton
You can go for below code
`
let btn: UIButton = UIButton(frame: CGRect(x: 100, y: 400, width: 100, height: 50))
btn.backgroundColor = UIColor.green
btn.setTitle("Click Me", for: .normal)
btn.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
btn.tag = 1
self.view.addSubview(btn)
for action
#objc func buttonAction(sender: UIButton!) {
let btnsendtag: UIButton = sender
if btnsendtag.tag == 1 {
dismiss(animated: true, completion: nil)
}
}
let button = UIButton(type: UIButtonType.Custom) as UIButton
button.addTarget(self, action: "action:", forControlEvents: UIControlEvents.TouchUpInside)
//then make a action method :
func action(sender:UIButton!) {
print("Button Clicked")
}
You need to add a Target to the button like Muhammad suggest
button.addTarget(self, action: "action:", forControlEvents: UIControlEvents.TouchUpInside)
But also you need a method for that action
func action(sender: UIButton) {
// Do whatever you need when the button is pressed
}
For swift 5 users can do by this simple way
cell.followButton.tag = 10
cell.followButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
#objc func buttonAction(sender: UIButton!) {
let id = sender.tag
print(id)
}
in addition to the above, the new ios14 introduce
if #available(iOS 14.0, *) {
button.addAction(UIAction(title: "Click Me", handler: { _ in
print("Hi")
}), for: .touchUpInside)
} else {
// Fallback on earlier versions
}
override func viewDidLoad() {
super.viewDidLoad()
let btn = UIButton()
btn.frame = CGRectMake(10, 10, 50, 50)
btn.setTitle("btn", forState: .Normal)
btn.setTitleColor(UIColor.redColor(), forState: .Normal)
btn.backgroundColor = UIColor.greenColor()
btn.tag = 1
btn.addTarget(self, action: "btnclicked:", forControlEvents: .TouchUpInside) //add button action
self.view.addSubview(btn)
}
For Swift 4 use following:
button.addTarget(self, action: #selector(AwesomeController.coolFunc(_:)), for: .touchUpInside)
//later in your AswesomeController
#IBAction func coolFunc(_ sender:UIButton!) {
// do cool stuff here
}

Keep UIButton Selected/Highlighted after touch

I'd like my button to remain highlighted after the user taps it. If the user taps the button again I'd like it to become de-selected/unhighlighted. I'm not sure how to go about doing this in swift. I'm currently setting the button highlight image and selected image to the same .png using interface builder.
When I run the app and tap the button, it changes to my highlight image for as long as my finger remains on the button.
Use below code
declare isHighLighted as instance variable
//write this in your class
var isHighLighted:Bool = false
override func viewDidLoad() {
let button = UIButton(type: .system)
button.setTitle("Your title", forState: UIControlState.Normal)
button.frame = CGRectMake(0, 0, 100, 44)
self.view.addSubview(button as UIView)
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
}
func buttonClicked(sender:UIButton)
{
dispatch_async(dispatch_get_main_queue(), {
if isHighLighted == false{
sender.highlighted = true;
isHighLighted = true
}else{
sender.highlighted = false;
isHighLighted = false
}
});
}
I would recomend to use selected state instead of highlighted the below code demonstarate with selected state
override func viewDidLoad() {
let button = UIButton(type: .system)
button.setTitle("Your title", forState: UIControlState.Normal)
button.frame = CGRectMake(0, 0, 100, 44)
self.view.addSubview(button as UIView)
//set normal image
button.setImage(normalImage, forState: UIControlState.Normal)
//set highlighted image
button.setImage(selectedImage, forState: UIControlState.Selected)
button.addTarget(self, action: "buttonClicked:", forControlEvents: UIControlEvents.TouchUpInside)
}
func buttonClicked(sender:UIButton)
{
sender.selected = !sender.selected;
}
func buttonPressed(_ sender: UIButton) {
// "button" is a property
if button.isSelected {
button.setImage(UIImage(named: "filled-heart"), for: .normal)
button.isSelected = false
}else {
button.setImage(UIImage(named: "empty-heart"), for: .selected)
button.isSelected = true
}
}
func highlightButton(button: UIButton) {
button.highlighted = true
}
#IBAction func touched(sender: UIButton) {
let timer = NSTimer.scheduledTimerWithTimeInterval(0.0, target: self, selector: Selector("highlightButton(sender)"), userInfo: nil, repeats: true)
}
this one worked fine for me!
func buttonColorChanger(sender : UIButton ) {
if button.isSelected == false
{
button.backgroundColor = UIColor.purple
print("selected")
button.setTitle("selected", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.isSelected = true
}else{
button.backgroundColor = UIColor.white
print("unselected")
button.isSelected = false
}
}
Swift 5:
#IBAction func toggleButton(_ sender: UIButton) {
sender.isSelected = !sender.isSelected
}

Resources