UIBarButtonItem in navigation bar programmatically?
Asked Answered
M

12

160

I've been looking around for this solution for a while but haven't got any. e.g one solution is

 self.navigationItem.setRightBarButtonItem(UIBarButtonItem(barButtonSystemItem: .Stop, target: self, action: nil), animated: true)

This code will add a button with "stop" image. Just like this, there are other solutions with "search, "refresh" etc. But what if I want to add a button programmatically with the image I want?

Mcilwain answered 4/5, 2015 at 4:45 Comment(0)
G
376

Custom button image without setting button frame:

You can use init(image: UIImage?, style: UIBarButtonItemStyle, target: Any?, action: Selector?) to initializes a new item using the specified image and other properties.

let button1 = UIBarButtonItem(image: UIImage(named: "imagename"), style: .plain, target: self, action: Selector("action")) // action:#selector(Class.MethodName) for swift 3
self.navigationItem.rightBarButtonItem  = button1

Check this Apple Doc. reference


UIBarButtonItem with custom button image using button frame

FOR Swift 3.0

    let btn1 = UIButton(type: .custom)
    btn1.setImage(UIImage(named: "imagename"), for: .normal)
    btn1.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
    btn1.addTarget(self, action: #selector(Class.Methodname), for: .touchUpInside)
    let item1 = UIBarButtonItem(customView: btn1)

    let btn2 = UIButton(type: .custom)
    btn2.setImage(UIImage(named: "imagename"), for: .normal)
    btn2.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
    btn2.addTarget(self, action: #selector(Class.MethodName), for: .touchUpInside)
    let item2 = UIBarButtonItem(customView: btn2)  

    self.navigationItem.setRightBarButtonItems([item1,item2], animated: true)

FOR Swift 2.0 and older

let btnName = UIButton()
btnName.setImage(UIImage(named: "imagename"), forState: .Normal)
btnName.frame = CGRectMake(0, 0, 30, 30)
btnName.addTarget(self, action: Selector("action"), forControlEvents: .TouchUpInside)

//.... Set Right/Left Bar Button item
let rightBarButton = UIBarButtonItem()
rightBarButton.customView = btnName
self.navigationItem.rightBarButtonItem = rightBarButton

Or simply use init(customView:) like

 let rightBarButton = UIBarButtonItem(customView: btnName)
 self.navigationItem.rightBarButtonItem = rightBarButton

For System UIBarButtonItem

let camera = UIBarButtonItem(barButtonSystemItem: .Camera, target: self, action: Selector("btnOpenCamera"))
self.navigationItem.rightBarButtonItem = camera

For set more then 1 items use rightBarButtonItems or for left side leftBarButtonItems

let btn1 = UIButton()
btn1.setImage(UIImage(named: "img1"), forState: .Normal)
btn1.frame = CGRectMake(0, 0, 30, 30)
btn1.addTarget(self, action: Selector("action1:"), forControlEvents: .TouchUpInside)
let item1 = UIBarButtonItem()
item1.customView = btn1

let btn2 = UIButton()
btn2.setImage(UIImage(named: "img2"), forState: .Normal)
btn2.frame = CGRectMake(0, 0, 30, 30)
btn2.addTarget(self, action: Selector("action2:"), forControlEvents: .TouchUpInside)
let item2 = UIBarButtonItem()
item2.customView = btn2

self.navigationItem.rightBarButtonItems = [item1,item2]

Using setLeftBarButtonItem or setRightBarButtonItem

let btn1 = UIButton()
btn1.setImage(UIImage(named: "img1"), forState: .Normal)
btn1.frame = CGRectMake(0, 0, 30, 30)
btn1.addTarget(self, action: Selector("action1:"), forControlEvents: .TouchUpInside)
self.navigationItem.setLeftBarButtonItem(UIBarButtonItem(customView: btn1), animated: true);

For swift >= 2.2 action should be #selector(Class.MethodName) ... for e.g. btnName.addTarget(self, action: #selector(Class.MethodName), forControlEvents: .TouchUpInside)

Gabbert answered 4/5, 2015 at 4:53 Comment(2)
Great answer. Just want to point out that the Selector method has been updated though to let camera = UIBarButtonItem(barButtonSystemItem: .Camera, target: self, action: #selector(Class.MethodName))Boarding
I found that if you want your button to also include the text you must specify the CGRect portion. There are some examples floating around SO that don't specify this.Meliamelic
S
53

It's much easier with Swift 4 or Swift 4.2

inside your ViewDidLoad method, define your button and add it to the navigation bar.

override func viewDidLoad() {
    super.viewDidLoad()

    let logoutBarButtonItem = UIBarButtonItem(title: "Logout", style: .done, target: self, action: #selector(logoutUser))
    self.navigationItem.rightBarButtonItem  = logoutBarButtonItem

}

then you need to define the function that you mentioned inside action parameter as below

@objc func logoutUser(){
     print("clicked")
}

You need to add the @objc prefix as it's still making use of the legacy stuff (Objective C).

Settler answered 27/8, 2018 at 11:40 Comment(0)
F
17

Just setup UIBarButtonItem with customView

For example:

  var leftNavBarButton = UIBarButtonItem(customView:yourButton)
  self.navigationItem.leftBarButtonItem = leftNavBarButton

or use setFunction:

  self.navigationItem.setLeftBarButtonItem(UIBarButtonItem(customView: yourButton), animated: true);
Fivefold answered 4/5, 2015 at 4:48 Comment(1)
I am up voting your answer as your code works as well but Bhavin's code is a one shot solution so I'll accept his answer. Thanks for your contribution mate :)Mcilwain
S
12

I just stumbled upon this question and here is an update for Swift 3 and iOS 10:

let testUIBarButtonItem = UIBarButtonItem(image: UIImage(named: "test.png"), style: .plain, target: self, action: nil)
self.navigationItem.rightBarButtonItem  = testUIBarButtonItem

It is definitely much faster than creating the UIButton with all the properties and then subsequently adding the customView to the UIBarButtonItem.

And if you want to change the color of the image from the default blue to e.g. white, you can always change the tint color:

test.tintColor = UIColor.white()

PS You should obviously change the selector etc. for your app :)

Stale answered 16/7, 2016 at 15:31 Comment(0)
M
8

In Swift 3.0+, UIBarButtonItem programmatically set up as follows:

   override func viewDidLoad() {
        super.viewDidLoad()
        let testUIBarButtonItem = UIBarButtonItem(image: UIImage(named: "test.png"), style: .plain, target: self, action: #selector(self.clickButton))
        self.navigationItem.rightBarButtonItem  = testUIBarButtonItem
    }

   @objc func clickButton(){
            print("button click")
     }
Misshapen answered 13/7, 2017 at 8:41 Comment(0)
S
7

FOR Swift 5+

let searchBarButtonItem = UIBarButtonItem(image: UIImage(named: "searchIcon"), style: .plain, target: self, action: #selector(onSearchButtonClicked))
        self.navigationItem.rightBarButtonItem  = searchBarButtonItem

@objc func onSearchButtonClicked(_ sender: Any){
    print("SearchButtonClicked")
}
Sphenoid answered 8/2, 2021 at 6:23 Comment(0)
H
3

Setting LeftBarButton with Original Image.

let menuButton = UIBarButtonItem(image: UIImage(named: "imagename").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(classname.functionname))
self.navigationItem.leftBarButtonItem  = menuButton
Hyetal answered 19/10, 2017 at 17:18 Comment(0)
O
3

iOS 11

Setting a custom button using constraint:

let buttonWidth = CGFloat(30)
let buttonHeight = CGFloat(30)

let button = UIButton(type: .custom)
button.setImage(UIImage(named: "img name"), for: .normal)
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)
button.widthAnchor.constraint(equalToConstant: buttonWidth).isActive = true
button.heightAnchor.constraint(equalToConstant: buttonHeight).isActive = true

self.navigationItem.rightBarButtonItem = UIBarButtonItem.init(customView: button)
Owner answered 20/12, 2019 at 18:29 Comment(0)
C
2

I have same issue and I have read answers in another topic then I solve another similar way. I do not know which is more effective. similar issue

//play button

@IBAction func startIt(sender: AnyObject) {
    startThrough();
};

//play button

func startThrough() {
    timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("updateTime"), userInfo: nil, repeats: true);

    let pauseButton = UIBarButtonItem(barButtonSystemItem: .Pause, target: self, action: "pauseIt");
    self.toolBarIt.items?.removeLast();
    self.toolBarIt.items?.append( pauseButton );
}

func pauseIt() {
    timer.invalidate();

    let play = UIBarButtonItem(barButtonSystemItem: .Play, target: self, action: "startThrough");
    self.toolBarIt.items?.removeLast();
    self.toolBarIt.items?.append( play );
}
Cordwood answered 20/5, 2016 at 10:47 Comment(0)
O
2

addition to the above you may use the following for ios14 and above

   if #available(iOS 14.0, *) {
        let closeAction = UIAction(handler: { [weak self] _ in
           //perform action here
        })
        let closeBarButtonItem = UIBarButtonItem(systemItem: .close, primaryAction: closeAction, menu: nil)
        navigationItem.rightBarButtonItem  = closeBarButtonItem
    }
Orleanist answered 22/6, 2021 at 15:20 Comment(0)
D
1
func viewDidLoad(){
let homeBtn: UIButton = UIButton(type: UIButtonType.custom)

        homeBtn.setImage(UIImage(named: "Home.png"), for: [])

        homeBtn.addTarget(self, action: #selector(homeAction), for: UIControlEvents.touchUpInside)

        homeBtn.frame = CGRect(x: 0, y: 0, width: 30, height: 30)

        let homeButton = UIBarButtonItem(customView: homeBtn)


        let backBtn: UIButton = UIButton(type: UIButtonType.custom)

        backBtn.setImage(UIImage(named: "back.png"), for: [])

        backBtn.addTarget(self, action: #selector(backAction), for: UIControlEvents.touchUpInside)

        backBtn.frame = CGRect(x: -10, y: 0, width: 30, height: 30)

        let backButton = UIBarButtonItem(customView: backBtn)
        self.navigationItem.setLeftBarButtonItems([backButton,homeButton], animated: true)
}

}
Docile answered 30/11, 2017 at 13:19 Comment(0)
M
1

This is a crazy thing of apple. When you say self.navigationItem.rightBarButtonItem.title then it will say nil while on the GUI it shows Edit or Save. Fresher likes me will take a lot of time to debug this behavior.

There is a requirement that the Item will show Edit in the firt load then user taps on it It will change to Save title. To archive this, i did as below.

//view did load will say Edit title

private func loadRightBarItem() {
    let logoutBarButtonItem = UIBarButtonItem(title: "Edit", style: .done, target: self, action: #selector(handleEditBtn))
    self.navigationItem.rightBarButtonItem  = logoutBarButtonItem
}

// tap Edit item will change to Save title

@objc private func handleEditBtn() {
    print("clicked on Edit btn")
    let logoutBarButtonItem = UIBarButtonItem(title: "Save", style: .done, target: self, action: #selector(handleSaveBtn))
    self.navigationItem.rightBarButtonItem  = logoutBarButtonItem
    blockEditTable(isBlock: false)
}

//tap Save item will display Edit title

@objc private func handleSaveBtn(){
    print("clicked on Save btn")
    let logoutBarButtonItem = UIBarButtonItem(title: "Edit", style: .done, target: self, action: #selector(handleEditBtn))
    self.navigationItem.rightBarButtonItem  = logoutBarButtonItem

    saveInvitation()
    blockEditTable(isBlock: true)

}
Manzo answered 17/3, 2019 at 8:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.