45

I am trying to change the size of some icons in my navBar, but I am a little confused as to how to do this? My code so far is:

func setUpNavBarButtons() {
    let moreButton = UIBarButtonItem (image: UIImage(named:"ic_more_vert_3")?.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleMore))
    navigationItem.rightBarButtonItems = [moreButton]
    let refreshButton = UIBarButtonItem (image: UIImage(named:"ic_refresh")?.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(refreshDataButton))
    navigationItem.leftBarButtonItems = [refreshButton]
}

any help?

Mayur Karmur
  • 2,119
  • 14
  • 35
Sole
  • 3,100
  • 14
  • 58
  • 112

8 Answers8

141

This is how I did it

iOS 10 and below

func setUpMenuButton(){
    let menuBtn = UIButton(type: .custom)
    menuBtn.frame = CGRect(x: 0.0, y: 0.0, width: 20, height: 20)
    menuBtn.setImage(UIImage(named:"menuIcon"), for: .normal)
    menuBtn.addTarget(self, action: #selector(vc.onMenuButtonPressed(_:)), for: UIControlEvents.touchUpInside)

    let menuBarItem = UIBarButtonItem(customView: menuBtn)
    self.navigationItem.leftBarButtonItem = menuBarItem
}

iOS 11 - Navigation bar come up with Autolayout so frame setting may not work

func setUpMenuButton(){
    let menuBtn = UIButton(type: .custom)
    menuBtn.frame = CGRect(x: 0.0, y: 0.0, width: 20, height: 20)
    menuBtn.setImage(UIImage(named:"menuIcon"), for: .normal)
    menuBtn.addTarget(self, action: #selector(vc.onMenuButtonPressed(_:)), for: UIControlEvents.touchUpInside)

    let menuBarItem = UIBarButtonItem(customView: menuBtn)
    let currWidth = menuBarItem.customView?.widthAnchor.constraint(equalToConstant: 24)
    currWidth?.isActive = true
    let currHeight = menuBarItem.customView?.heightAnchor.constraint(equalToConstant: 24)
    currHeight?.isActive = true
    self.navigationItem.leftBarButtonItem = menuBarItem
}
anoop4real
  • 7,598
  • 4
  • 53
  • 56
57

Extension for Swift 4.2

Just a different way of implementation the answer provided by anoop4real

However using button type .system allows the global tint to be applied to your icon

Usage:

navigationItem.leftBarButtonItem = UIBarButtonItem.menuButton(self, action: #selector(presentSettings), imageName: "settings")

Implementation:

extension UIBarButtonItem {

    static func menuButton(_ target: Any?, action: Selector, imageName: String) -> UIBarButtonItem {
        let button = UIButton(type: .system)
        button.setImage(UIImage(named: imageName), for: .normal)
        button.addTarget(target, action: action, for: .touchUpInside)

        let menuBarItem = UIBarButtonItem(customView: button)
        menuBarItem.customView?.translatesAutoresizingMaskIntoConstraints = false
        menuBarItem.customView?.heightAnchor.constraint(equalToConstant: 24).isActive = true
        menuBarItem.customView?.widthAnchor.constraint(equalToConstant: 24).isActive = true

        return menuBarItem
    }
}
DogCoffee
  • 19,820
  • 10
  • 87
  • 120
  • 1
    Thanks for this. For some reason the method in the top answer wasn't working for me like it usually does, but this works fine. – Jacob Hatwell Feb 06 '20 at 15:46
  • You should add this to your code to make sure the images appears correctly if it's not a perfect square: `button.imageView?.contentMode = .scaleAspectFit`. By the way, your answer is the only one that has worked for me with Swift 5 on iOS 14.4. Thank you. – Ethan Allen Mar 06 '21 at 04:45
  • Worked for me in iOS 14.5. Thank you bro, this should be the answer for the latest iOS versions. – Binaya Thapa Magar Oct 23 '21 at 14:07
10

You can configure the frame of you button like below:

let icon = UIImage(named: "imageName")
let iconSize = CGRect(origin: CGPoint.zero, size: CGSize(width: 50, height: 50))
let iconButton = UIButton(frame: iconSize)
iconButton.setBackgroundImage(icon, for: .normal)
let barButton = UIBarButtonItem(customView: iconButton)
iconButton.addTarget(self, action: #selector(foo), for: .touchUpInside)

navigationItem.leftBarButtonItem = barButton
Display Name
  • 4,502
  • 2
  • 47
  • 63
jokeman
  • 1,277
  • 9
  • 22
  • Hi I have tried to implement your code, but get the following error: Cannot convert value of type 'Int' to expected argument type 'CGSize'. What is the sizing configuration. Is it just size: 50 etc – Sole Mar 30 '17 at 09:32
  • @Sole It's a struct with width and height – jokeman Mar 30 '17 at 16:48
  • This works but you can't change the tint colour of the button – FedeH Aug 13 '19 at 15:06
  • 2
    @FedeHenze You need to create the `icon` like this `UIImage(named: "imageName")?.withRenderingMode(.alwaysTemplate)`. Then you will be able to set its tint. Look at [this](https://stackoverflow.com/questions/52981409/creating-an-image-with-uiimage-renderingmode/52981720#52981720) for more info. – Parth Feb 10 '21 at 16:34
6

@DogCoffee answer is a great and creative way to solve the problem. May I suggest some slightly mods in order to take into account size and tintColor

extension UIBarButtonItem {

    static func menuButton(_ target: Any?,
                           action: Selector,
                           imageName: String,
                           size:CGSize = CGSize(width: 32, height: 32),
                           tintColor:UIColor?) -> UIBarButtonItem
    {
        let button = UIButton(type: .system)
        button.tintColor = tintColor
        button.setImage(UIImage(named: imageName), for: .normal)
        button.addTarget(target, action: action, for: .touchUpInside)

        let menuBarItem = UIBarButtonItem(customView: button)
        menuBarItem.customView?.translatesAutoresizingMaskIntoConstraints = false
        menuBarItem.customView?.heightAnchor.constraint(equalToConstant: size.height).isActive = true
        menuBarItem.customView?.widthAnchor.constraint(equalToConstant: size.width).isActive = true

        return menuBarItem
    }
}
valvoline
  • 7,737
  • 3
  • 47
  • 52
5

In the end I did it like this and it worked:

let moreButton = UIButton(frame: CGRect(x: 0, y: 0, width: 35, height: 35))
moreButton.setBackgroundImage(UIImage(named: "ic_more_vert_3"), for: .normal)
moreButton.addTarget(self, action: #selector(TableViewController.handleMore), for: .touchUpInside)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: moreButton)

Answer from: Change width of a UIBarButtonItem in a UINavigationBar in swift

Community
  • 1
  • 1
Sole
  • 3,100
  • 14
  • 58
  • 112
4

If you are using SF symbol image, which is available from iOS 13, you could set the SF symbol image configuration.

let config = UIImage.SymbolConfiguration(pointSize: 30, weight: .light, scale: .default)

let image = UIImage(systemName: "plus.circle", withConfiguration: config)

Zhou Haibo
  • 1,681
  • 1
  • 12
  • 32
2

Swift 5 extension using button closure, expanding on @valvoline 's answer.

As you can appreciate instead of using target and selector Button allows you to pass a closure.

extension UIBarButtonItem {
static func imageButton(
    image: UIImage,
    size: CGFloat = 24,
    color: UIColor = .systemBackground,
    action: @escaping () -> Void
) -> UIBarButtonItem {
    // create ui button
    let button = UIButton(type: .system)
    // assign image
    button.setImage(image, for: .normal)
    button.tintColor = color
    // assign action
    button.addAction(.init(handler: { _ in action() }), for: .touchUpInside)
    // create menu bar item using custom view
    let menuBarItem = UIBarButtonItem(customView: button)
    //use auto layout to assign bar button's size
    menuBarItem.customView?.translatesAutoresizingMaskIntoConstraints = false
    NSLayoutConstraint.activate(
        [
            menuBarItem.customView?.heightAnchor.constraint(equalToConstant: size),
            menuBarItem.customView?.widthAnchor.constraint(equalToConstant: size)
        ].compactMap { $0 }
    )
    return menuBarItem
}
}

Usage Example:

let item = UIBarButtonItem.imageButton(image: image, color: .systemBlue) {
              print("You pushed it!") }
clearlight
  • 12,255
  • 11
  • 57
  • 75
vberihuete
  • 174
  • 5
0

You can configure the bar buttons using this function:

public convenience init(customView: UIView)

And You can init the custom view as You desire. After that You can access the view if needed via UIBarButtonItem's:

open var customView: UIView?

Hint: Because UIButton is a child class of UIView, You can directly use it too.

Georgi Boyadzhiev
  • 1,351
  • 1
  • 13
  • 18
  • Instead of using `let moreButton = UIBarButtonItem (image: UIImage(named:"ic_more_vert_3")?.withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleMore))` Create a UIButton and set the "ic_more_vert_3" image on it. Than use the `let moreButton = init(customView: theButtonThatYouCreated)` – Georgi Boyadzhiev Mar 30 '17 at 13:18