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

iOS 11中UIBarButtonItem调用setTitleTextAttributes无效,如何动态修改全局字体?

解决iOS 11中UIBarButtonItem动态修改字体的问题

我之前也踩过iOS 11里UIBarButtonItem外观代理设置不生效的坑,结合你动态修改字体的需求,整理了一套可行的解决方案:

一、先搞定iOS 11外观代理的生效问题

iOS 11里直接在didFinishLaunchingWithOptions里设置外观代理,有时候会因为视图布局时机问题导致不生效。建议把外观设置代码移到applicationDidBecomeActive中,或者设置后强制刷新导航栏:

func applicationDidBecomeActive(_ application: UIApplication) {
    // 先从UserDefaults读取上次存储的字体,没有则用默认自定义字体
    let defaultFont = UIFont(name: "YourDefaultCustomFont", size: 16)!
    let savedFontName = UserDefaults.standard.string(forKey: "SelectedBarButtonFont") ?? "YourDefaultCustomFont"
    let targetFont = UIFont(name: savedFontName, size: 16) ?? defaultFont
    
    UIBarButtonItem.appearance().setTitleTextAttributes([.font: targetFont], for: .normal)
    // 强制刷新所有导航栏的布局,确保字体生效
    UINavigationBar.appearance().layoutIfNeeded()
}

二、动态修改字体的完整流程

1. 存储用户选择的字体到UserDefaults

当用户选好字体后,把字体名称存起来(字体对象不能直接存,所以存名称最稳妥):

// 假设用户选择的字体名称是"UserSelectedFont"
UserDefaults.standard.set("UserSelectedFont", forKey: "SelectedBarButtonFont")
// iOS10+自动同步,手动调用更保险
UserDefaults.standard.synchronize()

2. 发送本地通知通知所有控制器更新

先定义一个专属的通知名称,方便所有需要的控制器监听:

extension Notification.Name {
    static let barButtonFontUpdated = Notification.Name("BarButtonFontUpdated")
}

// 存储字体后立刻发送通知
NotificationCenter.default.post(name: .barButtonFontUpdated, object: nil)

3. 让控制器监听通知并更新字体

建议写一个基类导航控制器,让所有需要的控制器继承它,统一处理字体更新:

class BaseNavController: UINavigationController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // 添加通知监听
        NotificationCenter.default.addObserver(self, selector: #selector(updateBarButtonFonts), name: .barButtonFontUpdated, object: nil)
        // 初始化时先加载一次存储的字体
        updateBarButtonFonts()
    }
    
    @objc private func updateBarButtonFonts() {
        let defaultFont = UIFont(name: "YourDefaultCustomFont", size: 16)!
        guard let savedFontName = UserDefaults.standard.string(forKey: "SelectedBarButtonFont"),
              let targetFont = UIFont(name: savedFontName, size: 16) else {
            // 没有存储的字体时用默认值
            resetToDefaultFont(defaultFont)
            return
        }
        
        // 更新全局外观代理
        UIBarButtonItem.appearance().setTitleTextAttributes([.font: targetFont], for: .normal)
        // 刷新当前导航栏的布局
        navigationBar.layoutIfNeeded()
        
        // 遍历当前所有子控制器,直接更新它们的导航栏按钮(避免缓存不刷新)
        viewControllers.forEach { vc in
            vc.navigationItem.leftBarButtonItems?.forEach {
                $0.setTitleTextAttributes([.font: targetFont], for: .normal)
            }
            vc.navigationItem.rightBarButtonItems?.forEach {
                $0.setTitleTextAttributes([.font: targetFont], for: .normal)
            }
        }
    }
    
    private func resetToDefaultFont(_ font: UIFont) {
        UIBarButtonItem.appearance().setTitleTextAttributes([.font: font], for: .normal)
        navigationBar.layoutIfNeeded()
        
        viewControllers.forEach { vc in
            vc.navigationItem.leftBarButtonItems?.forEach {
                $0.setTitleTextAttributes([.font: font], for: .normal)
            }
            vc.navigationItem.rightBarButtonItems?.forEach {
                $0.setTitleTextAttributes([.font: font], for: .normal)
            }
        }
    }
    
    deinit {
        // 销毁时移除监听,防止内存泄漏
        NotificationCenter.default.removeObserver(self)
    }
}

4. 极端情况的兜底方案

如果外观代理还是不生效,可以在创建UIBarButtonItem时直接设置字体:

// 在控制器里创建按钮时
let targetFont = UIFont(name: "UserSelectedFont", size: 16)!
let customBarButton = UIBarButtonItem(title: "操作", style: .plain, target: self, action: #selector(handleTap))
customBarButton.setTitleTextAttributes([.font: targetFont], for: .normal)
navigationItem.rightBarButtonItem = customBarButton

三、关键注意事项

  • iOS 11里UIBarButtonItem的外观设置依赖视图布局时机,layoutIfNeeded()是强制刷新的关键
  • 通知触发后,既要更新全局外观代理,也要直接遍历现有控制器的按钮设置属性,避免系统缓存导致的不刷新
  • 一定要在控制器销毁时移除通知监听,防止内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 10:20:39