今回はiOSでのWebViewについてまとめます
AndroidのWebViewについては下記まとめていますので、気になる方はみてください!!
※今回はUIkitです。SwiftUIはこの記事に追加するか、別で記事にします🙇
Webページを表示させる
Webページを表示させるにはWKWebView
を使用します。
今回、レイアウトはUIViewにWKWebViewをaddSubViewします。
下記がWKWebViewの基本的な使い方です。
WKWebView を利用する際はUIKitとWebKitをimportします。
WKWebView のinitializeはCGRectとWKWebViewConfiguration
を引数にとります。
該当するURLページを開くには、WKWebView の loadメソッドを実行します。
import UIKit import WebKit class WebViewController: UIViewController { var webView: WKWebView! override func viewDidLoad() { super.viewDidLoad() setupWebView() let url = URL(string:"https://www.apple.com") // URLRequestオブジェクトを生成 let myRequest = URLRequest(url: url!) // URLをWebView にロード webView.load(myRequest) } private func setupWebView() { let userContentController = WKUserContentController() userContentController.add(self, name: "testCallBack") let webConfiguration = WKWebViewConfiguration() let customFrame = CGRect( origin: CGPoint.zero, size: webViewContainer.frame.size ) webConfiguration.userContentController = userContentController webView = WKWebView(frame: customFrame, configuration: webConfiguration) webView.translatesAutoresizingMaskIntoConstraints = false webView.uiDelegate = self webView.navigationDelegate = self webView.scrollView.backgroundColor = .clear // webViewContainerは自前で用意したUIView(名前は自由に設定してください) webViewContainer.addSubview(webView) webView.topAnchor.constraint(equalTo: webViewContainer.topAnchor).isActive = true webView.rightAnchor.constraint(equalTo: webViewContainer.rightAnchor).isActive = true webView.leftAnchor.constraint(equalTo: webViewContainer.leftAnchor).isActive = true webView.bottomAnchor.constraint(equalTo: webViewContainer.bottomAnchor).isActive = true webView.heightAnchor.constraint(equalTo: webViewContainer.heightAnchor).isActive = true } } extension ViewController: WKUIDelegate { } extension ViewController: WKNavigationDelegate { }
この設定で表示させられます。ここからは細かい設定について説明します。
WebViewの生成の補足
WKWebViewConfiguration
WKWebViewConfiguration
はWKWebViewの初期化時に参照される設定プロパティが含まれるクラスです。
WKWebViewConfigurationを使用すると、Webページがレンダリングされるまでの時間、メディア再生の処理方法、ユーザーが選択できるアイテムの細分などのオプションを決定できます。
WKWebViewConfigurationは、WKWebViewが最初に初期化されたときにのみ使用されます。 このクラスを使用して、WKWebViewの作成後にその設定を変更することはできません。
WKUserContentController
WKUserContentController
はJavaScriptからのコールバック受信や、スクリプトをWebビューに挿入する方法を提供します。
必要であれば、WKWebViewConfiguration に生成したWKUserContentControllerを登録する処理を追加してください。
基本的な使い方の箇所で、userContentController.add(self, name: "testCallBack")のようにコールバックを登録しています。 具体的なコールバックの処理は下記のように追加します。
extension WebViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if(message.name == "testCallBack") { print(" \(message.body)") } } }
これでJavascriptからメッセージが送られてきたらアプリで受け取って処理を行うことができます。
WKUIDelegateとWKNavigationDelegate
基本的な使い方の箇所でWKUIDelegate
とWKNavigationDelegate
の設定を行いました。
ここでは、WKUIDelegateとWKNavigationDelegateについて補足します。
まず、WKUIDelegateはWebビューのUIを制御するデリゲートです。 主にウィンドウ、Javascriptのダイアログ(alert, confirm, prompt)、コンテキストメニューを制御します。
下記に例を記載します。
extension WebViewController: WKUIDelegate { // Webビューを作成 func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { guard let url = navigationAction.request.url else { return nil } guard let targetFrame = navigationAction.targetFrame, targetFrame.isMainFrame else { // 同一ページでWebを読み込む webView.load(URLRequest(url: url)) return nil } return nil } // Javascriptのalertダイアログを表示する func webView( _ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void ) { let alertController = UIAlertController( title: "", message: message, preferredStyle: .alert ) alertController.addAction(UIAlertAction(title: "OK", style: .default) {action in completionHandler() }) present(alertController, animated: true, completion: nil) } }
他にもデリゲート処理はありますが、公式ドキュメント等を参考にしてみてください。
WKNavigationDelegateはwebビューのナビゲーションのデリゲートです。 遷移開始時・開始時・ページの読み込み完了時・読み込みエラー発生時リダイレクト時などのイベントを受け取りたい場合はWKNavigationDelegateを継承します。
下記に例を記載します。
extension WebViewController: WKNavigationDelegate { // リクエストを許可するか判定 func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { decisionHandler(.allow) } // コンテンツを読み込んだ時に呼ばれるメソッド func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { } }
この例では、特定のURLのみ許可するのに使用したり、コンテンツを読み込んだ後に何か処理を行う場合に使用します。
HTMLを表示させる
次にHTMLを表示させる方法です
HTMLファイルを置いたら、Webページと同じように、ロードするだけで表示できます。
func loadLocalHTML() { guard let path: String = Bundle.main.path(forResource: "index", ofType: "html") else { return } let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false) webView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl) }
Bundle.main.pathはプロジェクトのBuild PhasesのCopy Bundle Resources
にあるファイルを取得するはずです。
また、HTMLのファイルではなく、文字列を読み込む場合にはloadHTMLStringメソッドを使用します。
func loadLocalHTML() { let webView = WKWebView() webView.loadHTMLString("<html><body><p>Hello!</p></body></html>", baseURL: nil) }
CSSの適応
CSSを適用させるにはページ読み込み完了したらCSSファイルを読み込む処理を行います
まずはHTML読み込み時にCSSも読み込むパターンです。
private func loadHTML() { guard let html = Bundle.main.url(forResource: "index"(ファイル名), withExtension: "html") else { return } guard let htmlString = try? String(contentsOf: html) else { return } /// CSSのファイルURLを取得する guard let css = Bundle.main.url(forResource: "style"(ファイル名), withExtension: "css") else { return } webView.loadHTMLString(htmlString, baseURL: css) }
ただ、CSSファイルを読み込むパターンはうまく適用されない場合もあります。
そこで、HTML表示後にCSSを適用させるパターンも紹介します。
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { let css = "img {max-width: 100%; width: 100%; height: 75%; vertical-align: middle;}" let js = "var style = document.createElement('style'); style.innerHTML = '\(css)'; document.head.appendChild(style);" cssWebView.evaluateJavaScript(js, completionHandler: nil) }
WKNavigationDelegateのdidFinish(表示完了時)にCSSを含めたJavascriptを実行する方法です。
evaluateJavaScript
メソッドはJavascriptを実行するメソッドです。
また、下記のようにスクリプトを追加することで表示前にCSSを適用するパターンもあります。
let script = WKUserScript(source: "JavaScriptコード", injectionTime: .atDocumentEnd, forMainFrameOnly: true) webView.configuration.userContentController.addUserScript(script) let controller = WKUserContentController() controller.addUserScript(script) let configuration = WKWebViewConfiguration() configuration.userContentController = controller let webView = WKWebView(frame: view.bounds, configuration: configuration)
CSSの適用は場合によってはできない場合にもあるので注意が必要です。
Javascriptとの連携
Javascriptからアプリの処理を実行したり、アプリからJavaScriptのメソッドを実行したい場合があると思います。 それぞれの方法を紹介します。
Javascriptからアプリの処理を実行する
Javascriptでボタンのタップアクションをアプリ側で受け取りアプリで処理を行う方法について説明します。 WKUserContentControllerの説明と多少、重複します。
HTMLは次の例を使用します。
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> </head> <body> <script type="text/javascript"> function showMessage() { webkit.messageHandlers.showMessage.postMessage(""); } function setMessage(message) { document.getElementById("text").innerHTML = message; } </script> <input type="button" value="Button" onClick="showMessage();" /> <p id="text"></p> </body> </html>
基本的な使い方の箇所でコールバックの設定を行いましたが、同様にハンドラを追加します。
let config: WKWebViewConfiguration = WKWebViewConfiguration() let controller: WKUserContentController = WKUserContentController() // JavaScriptから呼び出せるメッセージハンドラを設定する controller.add(self, name: "showMessage") config.userContentController = controller webView = WKWebView(frame: self.view.bounds, configuration: config) // 既にwebViewを初期化している場合は、こちらでもよい webView.configuration.userContentController.add(self, name: "showMessage")
WKScriptMessageHandlerに準拠して、上で定義したメッセージハンドラをJavaScriptから呼び出すと、userContentController(_:didReceive:)
が実行され、メッセージに応じた処理ができます。
extension WebViewController: WKScriptMessageHandler { func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { if message.name == "showMessage" { print("\\\\(message.body)") } } }
message.bodyはHTMLでwebkit.messageHandlers.showMessage.postMessage("");
の箇所のpostMessageで設定した文字列になります。
アプリからJavaScriptのメソッドを実行する
今度は先程と逆でアプリからJavascriptを実行する方法です。
次のようにevaluateJavascriptメソッドでJavascriptの関数を呼び出します
let message = "Message" let jsFunc = "test(\\\\"\\\\(message)\\\\")" // JavaScriptメソッドを実行する webView.evaluateJavaScript(jsFunc, completionHandler: { (object, error) -> Void in // JavaScriptメソッドの実行結果を受け取れる })