UIBarButtonItem 通过故事板放置与以编程方式放置时位置不同

UIBarButtonItem position different when placed via storyboard vs. programmatically

在我的应用程序中,我有一个带有 UIBarButtonItems 的工具栏。
在大多数情况下,UIBarButtonItems 是通过故事板设置的,如下所示:

在特殊情况下,我必须以编程方式替换一个 UIBarButtonItem。这是通过以下代码完成的:

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

当旋转按钮显示在工具栏中时,它被放置在不同的位置。它向右移动,如您在此处所见:

我怎样才能把两个 UIBarButtonItems 放在同一个位置?

编辑:
到现在为止,我意识到以编程方式创建的 UIBarButtonItem 的水平移动并不总是相同的,没有对代码进行任何更改:有时它向左移动,而不是向右移动:

编辑 2:
我找到了解决方法:
如果我为我的按钮设置宽度限制,例如

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

然后按钮显然总是正确放置。但我讨厌像这样硬编码约束。
有没有更优雅的方法呢?

尝试以下步骤来执行您的任务:

  1. 将左栏按钮项存储到 NSMutableArray
  2. 替换所需的UIBarbuttonItem
  3. 将 leftbarbuttonitems 设置为此新数组

希望这些步骤能奏效

当您以编程方式在 UIBarButton 上设置图像时,leftBarButtonItems 的内容模式变为 'left',rightBarButtonItems 变为 'right'。但是从故事板来看,它是居中的。设置图片并根据需要调整contentMode。

导航栏和工具栏都正常工作

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

}

这是迄今为止我找到的最佳解决方案:

使用键值编码获取另一个条形按钮项的视图宽度。这是来自 Jeremy W. Sherman 的回答 here.
请注意,它不使用任何私有 API,请参阅此处的讨论。可能发生的最糟糕的事情是无法访问 UIBarButtonItemview 属性。在这种情况下,我使用默认值:

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