0

In my app, I have a toolbar with UIBarButtonItems.
In most circumstances, the UIBarButtonItems are set via storyboard, and look as follows:

enter image description here

In a special case, I have to replace one UIBarButtonItem programmatically. This is done with the following code:

let rotatingButton = UIButton(type: .custom)
rotatingButton.setImage(UIImage(named: "LocalizationInUseNoFix"), for: .normal)
rotatingButton.addTarget(self, action: #selector(localizationButtonTapped), for: .touchUpInside)
rotatingButton.rotateStart()
let barButtonItem = UIBarButtonItem(customView: rotatingButton)
leftBarButtonItems![2] = barButtonItem

When the rotatingButton is displayed in the toolbar, it placed at a different position. It is shifted to the right, as you can see here:

enter image description here

How can I achieve to place both UIBarButtonItems at the same position?

EDIT:
By now I realized that the horizontal shift of the programmatically created UIBarButtonItem is not always the same, without any changes to the code: Sometimes it is shifted left, and not right:

enter image description here

EDIT 2:
I found a workaround:
If I set a width constrain to my button like

rotatingButton.widthAnchor.constraint(equalToConstant: 40).isActive = true

then the button is apparently always correctly placed. But I hate to hard-code constraints like this.
Is there a more elegant way to do it?

Reinhard Männer
  • 14,022
  • 5
  • 54
  • 116

5 Answers5

0

Try the below steps to perform your task:

  1. Store left bar button items into an NSMutableArray
  2. Replace desired UIBarbuttonItem
  3. Set leftbarbuttonitems to this new array

Hope this steps will work

storaged
  • 1,837
  • 20
  • 34
Jeetendra Kumar
  • 500
  • 3
  • 9
0

When you set the image on UIBarButton programmatically, the contentmode of the leftBarButtonItems becomes 'left' and rightBarButtonItems become 'right'. But from storyboard, it is centered. Set the image and adjust the contentMode as required.

  • Thanks for your answer, but I am not sure if I understood it right. What I tested is to set `rotatingButton.contentMode` in my code above to `.right`, `.left`and `.center`, but this had no effect to the placement of the button. – Reinhard Männer Dec 09 '17 at 16:05
  • Sorry, my mistake, It is not contentMode, it is "contentHorizontalAlignment" for UIButton & UIBarButton –  Dec 14 '17 at 05:49
0

All are working fine for Navigationbar and Toolbar

class ViewController: UIViewController {

@IBOutlet weak var toolbar: UIToolbar!
override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


@IBAction func leftAction(_ sender: Any) {
}

@IBAction func rightAction(_ sender: Any) {

}

@IBAction func changeLeftItems(_ sender: Any) {
    if let items = self.navigationItem.leftBarButtonItems {
        var addItems = [UIBarButtonItem]()
        addItems.append(contentsOf: items)
        let barItem = UIBarButtonItem(title: "3", style: UIBarButtonItemStyle.plain, target: self, action: #selector(ViewController.leftAction(_:)))
        addItems.append(barItem)
        self.navigationItem.leftBarButtonItems = addItems
    }
    if let items = self.toolbar.items {
        var addItems = [UIBarButtonItem]()
        addItems.append(contentsOf: items)
        let barItem = UIBarButtonItem(title: "L3", style: UIBarButtonItemStyle.plain, target: self, action: #selector(ViewController.leftAction(_:)))
        addItems.insert(barItem, at: 2)
        self.toolbar.setItems(addItems, animated: true)
    }
}

}

Jeetendra Kumar
  • 500
  • 3
  • 9
  • Your code sets UIBarButtonItems into the toolbar, but it does not at all address my problem, how to replace a bur button item programmatically **in place**. – Reinhard Männer Dec 10 '17 at 08:08
  • @IBAction func changeLeftItems(_ sender: Any) { if let items = self.toolbar.items { var addItems = [UIBarButtonItem]() addItems.append(contentsOf: items) let barItem = UIBarButtonItem(title: "L5", style: UIBarButtonItemStyle.plain, target: self, action: #selector(ViewController.leftAction(_:))) addItems.remove(at: 1) addItems.insert(barItem, at: 1) self.toolbar.setItems(addItems, animated: true) } } – Jeetendra Kumar Dec 10 '17 at 17:08
0

This is the best solution I found so far:

Get the width of a view of another bar button item using key value coding. This is from Jeremy W. Sherman’s answer here.
Please note that it does not use any private API, see the discussion there. The worst thing that can happen is that the view property of the UIBarButtonItem cannot be accessed. In this case, I use a default value:

var leftBarButtonItems = self.navigationItem.leftBarButtonItems

let rotatingButton = UIButton(type: .custom)
rotatingButton.setImage(UIImage(named: "LocalizationInUseNoFix"), for: .normal)
rotatingButton.addTarget(self, action: #selector(localizationButtonTapped), for: .touchUpInside)
rotatingButton.rotateStart()

// Get the width of the bar button items if possible, else set default
let leftmostBarButtonItem = leftBarButtonItems![0]
let barButtonItemWidth: CGFloat
if let leftmostBarButtonItemView = leftmostBarButtonItem.value(forKey: "view") as? UIView {
  barButtonItemWidth = leftmostBarButtonItemView.frame.size.width
} else {
  barButtonItemWidth = 40.0
}
rotatingButton.widthAnchor.constraint(equalToConstant: barButtonItemWidth).isActive = true
let barButtonItem = UIBarButtonItem(customView: rotatingButton)
leftBarButtonItems![2] = barButtonItem
self.navigationItem.leftBarButtonItems = leftBarButtonItems
Reinhard Männer
  • 14,022
  • 5
  • 54
  • 116
0
This is working fine for me. Best way is identify item to replace and change the content    

@IBAction func changeLeftItems(_ sender: Any) {
            if let items = self.toolbar.items {
                var addItems = [UIBarButtonItem]()
                addItems.append(contentsOf: items)
                let barItem = UIBarButtonItem(title: "L5", style: UIBarButtonItemStyle.plain, target: self, action: #selector(ViewController.leftAction(_:)))
                addItems.remove(at: 1)
                addItems.insert(barItem, at: 1)
                self.toolbar.setItems(addItems, animated: true)
            }
        }
Jeetendra Kumar
  • 500
  • 3
  • 9
  • Thanks for your answer. If I see it right, you replace a `UIBarButtonItem` by a new one. But my problem is different (see my question above): I have to replace a `UIBarButtonItem` by a `UIBarButtonItem` with a custom view, and this custom view is placed at another place than the replaced one. – Reinhard Männer Dec 10 '17 at 17:42