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

iOS 14下UIBarButtonItem依赖上下文的菜单项配置方案问询

在iOS 14中动态配置UIBarButtonItem菜单(打开前完成上下文依赖的配置)

我之前也碰到过一模一样的问题——iOS14给UIBarButtonItem加菜单确实省心,但遇到菜单项要跟着实时上下文变的情况,直接写死menu就完全行不通了。你提到的几个方案里,其实UIDeferredMenuElement是能解决这个问题的,可能是之前用法没找对,我来给你捋捋正确的实现方式:

核心方案:正确使用UIDeferredMenuElement延迟生成菜单项

UIDeferredMenuElement的设计初衷就是应对「菜单内容依赖实时状态」的场景,它会在菜单即将弹出时才执行内容生成逻辑,完美适配你的需求。具体步骤如下:

  1. 初始化UIBarButtonItem时,给它设置一个包含UIDeferredMenuElement的基础菜单
  2. 在UIDeferredMenuElement的completion闭包里,根据当前上下文(比如选中内容、页面状态等)生成实际的菜单项,再通过completion回调返回

举个实际代码例子:

// 初始化UIBarButtonItem
let dynamicBarButton = UIBarButtonItem(title: "操作", style: .plain, target: nil, action: nil)

// 创建延迟加载的菜单元素
let deferredElement = UIDeferredMenuElement { [weak self] completion in
    guard let self = self else {
        completion([])
        return
    }
    
    // 这里获取当前上下文状态,比如从数据源或页面控件取实时值
    let currentSelectedItem = self.getCurrentSelectedItem()
    
    // 根据上下文生成对应的菜单项
    let dynamicMenuItems: [UIMenuElement] = [
        UIAction(title: "编辑「\(currentSelectedItem.name)」", handler: { _ in
            self.handleEditAction(for: currentSelectedItem)
        }),
        UIAction(title: "分享「\(currentSelectedItem.name)」", handler: { _ in
            self.handleShareAction(for: currentSelectedItem)
        }),
        // 可以根据上下文动态添加/隐藏菜单项
        currentSelectedItem.isEditable ? UIAction(title: "删除", attributes: .destructive, handler: { _ in
            self.handleDeleteAction(for: currentSelectedItem)
        }) : UIMenuElement()
    ].filter { !($0 is UIMenuElement) } // 过滤空元素
    
    // 将生成的菜单项传给completion,菜单会自动更新显示
    completion(dynamicMenuItems)
}

// 给BarButtonItem设置包含延迟元素的菜单
dynamicBarButton.menu = UIMenu(title: "", children: [deferredElement])

这样每次用户点击/长按弹出菜单时,都会触发闭包里的逻辑,根据当时的实时上下文生成最新的菜单项,完全不用在上下文变化时手动更新menu属性。

为什么你之前觉得UIDeferredMenuElement不适用?

大概率是你尝试直接用它替换整个menu,或者没有把它作为UIMenu的子元素来使用。记住,UIDeferredMenuElement是作为UIMenu的children存在的,它会在菜单即将显示时自动替换成你生成的实际菜单项,用户完全感知不到这个延迟过程。

备选方案:自定义UIButton作为customView

如果确实不想用UIDeferredMenuElement,也可以把UIButton作为UIBarButtonItem的customView,然后监听菜单即将显示的通知来动态更新:

let customButton = UIButton(type: .system)
customButton.setTitle("操作", for: .normal)
let barButton = UIBarButtonItem(customView: customButton)

// 监听菜单将要显示的通知
NotificationCenter.default.addObserver(forName: UIMenu.menuWillShowNotification, object: nil, queue: .main) { [weak self] _ in
    guard let self = self else { return }
    // 根据上下文生成新菜单
    let updatedMenu = UIMenu(title: "", children: self.generateDynamicMenuItems())
    customButton.menu = updatedMenu
}

// 记得在deinit时移除观察者
deinit {
    NotificationCenter.default.removeObserver(self)
}

不过这个方案需要处理通知的生命周期,不如UIDeferredMenuElement简洁,优先推荐前者。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 20:27:47