Removing Search Web from a context menu
Prevent a context menu from ignoring your Default Web Browser App
Context menu
iOS presents a context menu when selecting text on some views, like UITextView
and WKWebView
.
UIResponder
protocol allows us to edit the context menu before being presented, with the method buildMenu(with:).
Search Web
Among the options presented on the context menu, there is one for doing a web search of the selected text; indeed very convenient for most cases.
Default browser
Starting on iOS 14, third-party apps can become a default browser, and most web navigation will go through them, this is a “newish” feature and still not bulletproof or mature, though.
While there is a defined way to receive web navigation, i.e. a URL
with http
or https
scheme
, there is no scheme
, protocol, or deep link defined for web searches, yet.
The problem
Search Web option of the context menu does not know how to tell the default browser to do a web search; in fact, it is possible that app doesn’t have any searching capabilities, as this was not a requirement to become default browser; so this chore will go directly to the only app that for sure can fulfil it: Safari.
But maybe this is not ideal if your app can take care of it.
UIMenuBuilder
As mentioned above, we can intercept and edit a context menu before being presented, so the natural approach would be to edit the UIMenuBuilder
object received on buildMenu(with:)
; but wait, Search Web is not there… yet.
UIMenuBuilder
contains a collection of menus, each one of them with a collection of UIMenuElement
, some of them are a UICommand
or a UIAction
.
Interestingly enough, among the menus, we can find the Lookup menu (with the menu identifier lookup
), and this has the children Look Up, Find Selection and Translate; if we remove Look Up from the collection, then Search Web will not appear on the context menu at all. But that means losing the Look Up capabilities, which I like a lot.
How can we remove Search Web without removing Look Up?
Solution
The system edits the context menu before being presented and after giving you the chance to edit it on buildMenu(with:)
, if there is a menu with the identifier lookup
and with a UICommand
that has the selector _define:
, then it will insert the Search Web option, which is a UIAction
.
More specifically, it will replace the items on the menu with the same items + Search Web.
Let’s prevent this replacement then.
Subclass UIMenu
Override replacingChildren(_:) with an implementation that does nothing.
class MyMenu: UIMenu {
override func replacingChildren(
_ newChildren: [UIMenuElement]) -> UIMenu {
return self
}
}
Replace Look Up
Use your UIMenu
subclass configured with the properties of the Look Up menu.
override func buildMenu(with builder: UIMenuBuilder) {
guard let lookUp = builder.menu(for: .lookup) else { return }
let myMenu = MyMenu(title: lookUp.title,
identifier: lookUp.identifier,
options: lookUp.options,
children: lookUp.children)
builder.replace(menu: .lookup, with: myMenu)
}
Extra points
Once Search Web is removed, you could add your implementation for web searching with your own UICommand
or UIAction
; after all, having this on a context menu is quite handy.