简体   繁体   中英

UIBarButtonItem position different when placed via storyboard vs. programmatically

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

在此处输入图片说明

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:

在此处输入图片说明

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:

在此处输入图片说明

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?

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

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.

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

}

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM