[Swift]WKWebViewで長押しによるメニュー表示を止める

2021.04.27

CX事業本部の中安です。まいどです。

本日はiOSアプリ開発の小ネタになりますが、WKWebViewで「長押しによるメニュー表示を止める方法」を書き留めたいと思います。

iPhone や iPad を使っている方ならよく使うと思いますが、 ブラウザ上の文字を長押しすると、下図のようにPCでいうところの右クリックのように範囲選択してコピー等をするためのメニューが表示されます。

これはWKWebView。つまり、アプリ上のWEBビューでも同じ動作をします。

しかし、アプリによってはこのメニュー表示をさせたくないこともあるかもしれません。 今回は、このメニュー表示を制御してみることにしましょう。

前提となる実装

今記事ではUI実装については割愛しますが、 今回作る画面(名前をWebViewControllerとします)では WKWebViewが画面全体に配置されているものとします。

その場合の必要最低限な実装は以下のような感じです。

import UIKit
import WebKit

class WebViewController: UIViewController {

    @IBOutlet private weak var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        let url = URL(string: "URLがここに入る")!
        let request = URLRequest(url: url)
        webView.load(request)
    }
}

ちなみに今記事では "URLがここに入る"には、https://ja.wikipedia.org/wiki/Swift_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E) という Wikipedia の記事を入れています。

メニュー表示を止める仕組み

前項の実装のままであると、最初に書いたように長押しすることでメニューが表示されてしまいます。

これを止めるにはJavascriptCSSの仕組みを使用します。

読み込まれたHTMLドキュメントのヘッダに対して、Javascript を使って style要素を後から足してやるという方法です。 そのstyle要素に操作制御系のCSSを書いてやることで実現をしていきます。

では、ここに足していくべき操作制御系のCSSプロパティをご紹介します。

-webkit-user-select

The user-select CSS property controls whether the user can select text. This doesn't have any effect on content loaded as part of a browser's user interface (its chrome), except in textboxes.

user-select CSSプロパティは、ユーザーがテキストを選択可能かどうかをコントロールします。これは、テキストボックスを除き、ブラウザUI(Chrome)の一部として読み込まれるコンテンツに影響を与えません。

user-selectの中でも-webkit-user-selectsafariエンジンのブラウザ解釈に影響を与えます。 ですので、今回は-webkit-user-selectプロパティを使用していきます。

https://developer.mozilla.org/en/docs/Web/CSS/user-select

-webkit-touch-callout

The -webkit-touch-callout CSS property controls the display of the default callout shown when you touch and hold a touch target.

When a target is touched and held on iOS, Safari displays a callout information about the link. This property allows disabling that behavior.

-webkit-touch-callout CSSプロパティは、タッチターゲットを長押したときに表示されるデフォルトのコールアウト表示をコントロールします。

iOSでターゲットを長押しすると、Safariはリンクに関するコールアウト情報を表示します。 このプロパティは、その振る舞いを無効化できます。

ドキュメントによると、CSSとしては非標準ではありますが、iOSブラウザ上の制御のために用意されているCSSプロパティのようです。

「リンクに関するコールアウト情報」というのは、リンクを長押しした時に出る下図のような「プレビュー表示」と呼ばれるものです。

長押しでのメニューを非表示にするならば、このプレビュー表示(コールアウト表示)も制御しておきたいところです。

https://developer.mozilla.org/en/docs/Web/CSS/-webkit-touch-callout

extensionを定義

では、WKWebViewに対して以下のような extension を用意しましょう。

extension WKWebView {
    
    /// 長押しによる選択、コールアウト表示を禁止する
    func prohibitTouchCalloutAndUserSelect() {
        let script = """
        var css = '*{-webkit-touch-callout:none;-webkit-user-select:none}';
        var head = document.head || document.getElementsByTagName('head')[0];
        var style = document.createElement('style');
        style.type = 'text/css';
        style.appendChild(document.createTextNode(css));
        head.appendChild(style);
        """
        evaluateJavaScript(script, completionHandler: nil)
    }
}

前項でも述べたとおり、CSS<head>タグに埋め込むためのJavascriptをWEBビューに対して呼ばせます。

当ブログの仕様上、シンタックスハイライトが上手くいかないのですが、 上のコードで明るくハイライトしている部分はすべて Swift上では文字列となり、Javascriptのソースコードになります。

その部分だけ切り出してみましょう。以下はSwiftではくJavascriptとして読んでくださいね。

var css = '*{-webkit-touch-callout:none;-webkit-user-select:none}';
var head = document.head || document.getElementsByTagName('head')[0];
var style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(css));
head.appendChild(style);

HTMLのすべての要素に対して、先ほどのCSSプロパティをnoneで指定していることがわかると思います。

適用する

最初に書いた前提となる実装コードに、今回の処理を適用してみましょう。

import UIKit
import WebKit

class WebViewController: UIViewController {

    @IBOutlet private weak var webView: WKWebView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        webView.navigationDelegate = self
        let url = URL(string: "URLがここに入る")!
        let request = URLRequest(url: url)
        webView.load(request)
    }
}

extension WebViewController: WKNavigationDelegate {

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        webView.prohibitTouchCalloutAndUserSelect()
    }
}

明るくハイライトしたのが追記した部分になります。

このようにドキュメントが読み終わった時に、ドキュメントに対してCSSを追加することでメニュー表示を制御することができます。

アプリを起動して実際に長押しして確認してみてください。

最後に

この方法は、HTMLに対して要素を追加することで実現しているので、 例えばプレーンテキストであったり、その他HTML以外のドキュメントに対しては制御できないと思います。

また、WEBページによっては制御が効かない場合もあるかもしれません。 (数多くのWEBページで検証をしたわけではありません)

WEBビューに表示するコンテンツ要件などを検討のもと、 用途に合致しそうであればお試しいただければと思います。

それでは、また。