You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UIBarButtonItem字体粗细变化时如何固定其尺寸避免晃动?

问题说明

UIBarButtonItem切换字体粗细时会根据文本属性自动计算宽度,导致按钮整体偏移晃动,效果如下:
barButtonItem随标题尺寸变化而改变大小

解决方案

核心思路是固定UIBarButtonItem的宽度,不再随文本属性动态调整,推荐使用自定义customView的方案,兼容性最高、实现简单:

  1. 创建UIBarButtonItem时封装固定宽度的UIButton作为customView,宽度按照粗体文本的最大宽度预留,避免切换时空间不足
  2. 高亮逻辑调整为修改customView内UIButton的文本属性

修改后的完整可运行代码如下:

import UIKit

class ViewController: UIViewController {
    
    var barButtonItems: [UIBarButtonItem]!

    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .darkGray
        
        let navBar: UINavigationBar = UINavigationBar(frame: CGRect(x: 0, y: 100, width: view.frame.width, height: 40))
        navBar.barTintColor = #colorLiteral(red: 0.1019607857, green: 0.2784313858, blue: 0.400000006, alpha: 1)
        view.addSubview(navBar)
        
        let navItem = UINavigationItem()
        // 改为创建自定义按钮封装成UIBarButtonItem
        let appleBarBtnItem = createCustomBarBtn(title: "Apple", tag: 0)
        let bananaBarBtnItem = createCustomBarBtn(title: "Banana", tag: 1)
        let kiwiBarBtnItem = createCustomBarBtn(title: "Kiwi", tag: 2)
        let pearBarBtnItem = createCustomBarBtn(title: "Pear", tag: 3)
        let orangeBarBtnItem = createCustomBarBtn(title: "Orange", tag: 4)
        barButtonItems = [appleBarBtnItem, bananaBarBtnItem, kiwiBarBtnItem, pearBarBtnItem, orangeBarBtnItem]

        navItem.leftBarButtonItems = barButtonItems
        navBar.setItems([navItem], animated: false)
        
        setCurrentBarItem(appleBarBtnItem)
    }
    
    // 新增:创建固定宽度的自定义UIBarButtonItem
    func createCustomBarBtn(title: String, tag: Int) -> UIBarButtonItem {
        // 提前计算该标题用粗体显示时的宽度,加20作为左右内边距,也可以直接写固定值如80
        let boldFont = UIFont.systemFont(ofSize: 20, weight: .bold)
        let textWidth = title.size(withAttributes: [.font: boldFont]).width + 20
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: textWidth, height: 30))
        button.setTitle(title, for: .normal)
        button.tag = tag
        button.addTarget(self, action: #selector(barItemTapped), for: .touchUpInside)
        return UIBarButtonItem(customView: button)
    }
    
    func setCurrentBarItem(_ barBtnItem: UIBarButtonItem) {
        for btn in barButtonItems {
            highlight(btn, turnedOn: (btn == barBtnItem) )
        }
    }
    
    func highlight(_ button: UIBarButtonItem, turnedOn: Bool) {
        button.tintColor = #colorLiteral(red: 0.7230747342, green: 0.9554787278, blue: 0.9893732667, alpha: 1)
        let weight: UIFont.Weight = turnedOn ? .bold : .thin
        let attributes: [NSAttributedString.Key : Any] = [ .font: UIFont.systemFont(ofSize: 20, weight: weight)]
        // 改为修改customView中按钮的文本属性
        guard let customBtn = button.customView as? UIButton else { return }
        customBtn.setTitleTextAttributes(attributes, for: .normal)
    }
    
    @objc func barItemTapped(_ sender: UIButton) {
        let targetItem = barButtonItems[sender.tag]
        setCurrentBarItem(targetItem)
    }
}

如果不想使用自定义View,也可以在调用setTitleTextAttributes时,给所有状态的文本添加固定字间距,确保粗细切换时总宽度不变,但该方案适配不同文本长度的成本更高,更推荐上述自定义方案。

内容的提问来源于stack exchange,提问作者Kaplan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 01:39:01