High Sierra: Help Main Menu is Broken and Freezes Apps

If you run macOS 10.13 High Sierra, try not to use the Help menu. It will appear to freeze the app for which you invoke the Help menu: it will not accept keyboard input anymore, emitting the NSBeep “invalid action” sound in most circumstances.

The reason seems to be that the nifty Search bar inside the Help menu will acquire focus so you can search for a menu item or help book entry. This is taking away focus from the app window. You notice this when the blue focus ring around an active text field in the window goes away; instead, the Help menu’s Search bar obtains focus and the accompanying blue focus ring. All this is supposed to happen. This process just isn’t reverted for some reason since at least macOS 10.13.3, maybe earlier. When you click back into the app outside of the Help menu, the key window status will not be assigned back to the window you click on. That means keyboard input will not work at all. (As a non-native English speaker, I asked if “key window” was supposed to mean “window that responds to key events” a while back. While people agreed this wasn’t the intention, I still cannot help to think that way. The app has no key window, so key events are not handled, resulting in NSBeep.)

You can fix this inconsistent state yourself: open the Help menu again, then hover over an adjacent main menu entry, like “Window”. This will apparently close the Help menu in a different, proper way, unlike clicking back inside the main app’s window. Restarting the app works too. Cmd-tabbing out and back in does not help, though. (Backstage info: the Help menu will send willOpen when you tab back into the app, re-acquiring key status while not being on screen.)

I filed a bug report with Apple. See rdar://39374865 and make sure to clone the report so this is fixed sooner than later.

That’s a bad problem. As an app developer, your app will appear broken, so you need to fix this for your users even though it’s an Apple bug.

A working fix is to assign a NSMenuDelegate to the Help main menu item’s submenu (which consider to “be” the Help menu) and restore key status in the app. This is a pretty simple brute-force solution:

extension AppDelegate: NSMenuDelegate {
	func menuDidClose(_ menu: NSMenu) {
		if menu === NSApp.helpMenu {
			// Replace with more elaborate restoration:
			NSApp.mainWindow?.makeKey()
		}
	}
}

Setting the key window this way might actually result in a second menuDidClose(_:) call (after another menuDidOpen, by the way, which is odd).

Also, I found the window needs a reason to redraw afterwards to draw the blue focus ring where it belongs. Your app will forward key events to the current responder as expected, but if the firstResponder is a text field, the blue ring will not be visible immediately. I can live with this for now.

Also, I have yet to find out how multi-window apps or NSDocument-based apps behave. Is the right window restored? Do you want the mainWindow to become key? I’ll experiment with TableFlip and see what happens.

Today, Daniel Jalkut wrote about this, too, with a more elaborate approach to restore the actual state before the help menu was invoked.