and thanks in advance for your help. I assure you that I've read most everything on here about UIMenuController problems. I really think I've covered it all. Clearly I've missed something.
In a nutshell, I'm trying to replicate the "Replace ..." edit menu behavior (but with my own function different than Replace). (If you're not familiar, when a word is selected, the Replace... option in the edit menu will bring up a second menu which shows possible alternate spellings for the word.)
In a UITextView (sub-classed), I select some text. The default gesture recognizer causes the edit menu to come up with the expected items, including my added "Translate..." option. When I click on "Translate..." in the menu, the menu closes and invokes my selector code. That code changes the menu items to the sub-choices I want. I call
UIMenuController.shared.showMenu(from: self, rect: textBounds).
I see the calls to canPerformAction() to verify that the "sub-menu" items I've added are recognized, but the menu never shows up. The notification for willShowWindowNotification (which occurs when the first menu is opened) does not happen for this sub-menu.
Here is the code:
@objc func translateSelectionMenu()
{
let sharedMC = UIMenuController.shared
// Create menu choices for the translate sub-menu.
let charChoice = UIMenuItem(title: "To Chars", action: #selector(translateChars))
let byteChoice = UIMenuItem(title: "Byte Decimal", action: #selector(translateByte))
let halfChoice = UIMenuItem(title: "2-Byte Decimal", action: #selector(translateHalf))
savedMenuItems = sharedMC.menuItems
sharedMC.menuItems = [charChoice, byteChoice, halfChoice]
... for brevity, I've omitted the code here which determines the bounds of the user's
text selection. The resulting numbers are shown below.
let textBounds = CGRect(x: 114.1, y: 73, width: 48, height: 55)
// let windowBounds = convert(textBounds, to: nil)
// sharedMC.update() not needed
self.becomeFirstResponder() // TextView is already the first responder. This does nothing.
sharedMC.showMenu(from: self, rect: textBounds)
}
Note that the TextView IS and must remain first-responder. (Changing it loses the users selection.) So I've implemented all of this in the subclass of the UITextView that is showing the user's text. I have tried using the UITextView-referenced bounds and the window-referenced bounds but neither works.
If I move one of the end-points of the selected text or just click in the selection, this causes the menu to be shown again, and it has my sub-menu items in it as expected. I know this should work because "Replace..." does it all the time.
Things I've verified:
- My sub-class of UITextView is a UIView.
- UserInteractionIsEnabled is true (since I can select the text).
- There is only one window, but I am calling self.window.makeKeyAndVisible() at the point where canBecomeFirstResonder is called.
- I have implemented canBecomeFirstResponder() (returning True). (It is called right before the gesture recognizer brings up the first menu but not after that.)
- I do call self.becomeFirstResponder() (even though it already is).
- I have implemented canPerformAction(). This is called a lot both with first-menu and sub-menu items. I return True for the items I want to use.
What else? Thanks!!
I asked Apple for help on this. The fix is to add
right before the call to
showMenu().I think the issue is that my code is not what had presented the Menu originally and so I had to hide it before my code could show it. I note (from notifications) that the menu was not officially "hidden" at all (even though it was no longer visible after pressing my Translate... button).
I also tried just changing the menuItems and calling
update(), but that also didn't work, probably again for the same reason.