获取UIBarButtonItem菜单显示时的事件



我们都知道如何让一个简单的点击栏按钮项显示一个菜单(在iOS 14中引入):

let act = UIAction(title: "Howdy") { act in
print("Howdy")
}
let menu = UIMenu(title: "", children: [act])
self.bbi.menu = menu // self.bbi is the bar button item

到目前为止,一切顺利。但是,当点击栏按钮项时,显示菜单并不是我想要做的唯一的事情。只要菜单还在显示,我就需要暂停游戏计时器等等。所以我需要得到一个事件,告诉我按钮已经被点击了。

我不希望这个点击事件的与菜单的生成不同;例如,我不想将目标和操作附加到按钮上,因为如果我这样做,那么菜单的生成是一个不同的事情,只有当用户长按按钮时才会发生。我希望菜单出现在点击上,接收一个事件,告诉我这正在发生。

这一定是一个普遍的问题,那么人们是如何解决的呢?

我能找到的唯一方法是使用UIDeferredMenuElement在点击菜单时执行一些操作。然而,问题是,你必须重新创建整个菜单,并在延迟菜单元素的elementProvider块中再次将其分配给栏按钮项,以便获得未来的点击事件,正如你在这个玩具示例中看到的:

class YourViewController: UIViewController {

func menu(for barButtonItem: UIBarButtonItem) -> UIMenu {
UIMenu(title: "Some Menu", children: [UIDeferredMenuElement { [weak self, weak barButtonItem] completion in
guard let self = self, let barButtonItem = barButtonItem else { return }
print("Menu shown - pause your game timers and such here")

// Create your menu's real items here:
let realMenuElements = [UIAction(title: "Some Action") { _ in
print("Menu action fired")
}]

// Hand your real menu elements back to the deferred menu element
completion(realMenuElements)

// Recreate the menu. This is necessary in order to get this block to
// fire again on future taps of the bar button item.
barButtonItem.menu = self.menu(for: barButtonItem)
}])
}

override func viewDidLoad() {
super.viewDidLoad()

let someBarButtonItem = UIBarButtonItem(systemItem: .done)
someBarButtonItem.menu = menu(for: someBarButtonItem)
navigationItem.rightBarButtonItem = someBarButtonItem
}
}

此外,看起来在iOS 15中开始有一个类方法在UIDeferredMenuElement上称为uncached(_:),创建一个延迟的菜单元素,每次点击栏按钮项时触发其elementProvider块,而不仅仅是第一次,这意味着你不必像上面的例子那样重新创建菜单。