iOSアプリ開発 入門 (3) -WKWebView
iOSアプリの「WKWebView」による「Webビュー」の実装方法をまとめました。
・iOS 14
前回
1. WKWebView
iOSアプリでカスタマイズ可能な「Webビュー」を実装するには、「WKWebView」を使います。
2. Info.plist
「http通信」(https通信ではなく)を行うには、以下のタグをInfo.plistに追加する必要があります。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
<key>NSAllowsArbitraryLoadsForMedia</key>
<true/>
<key>NSAllowsArbitraryLoadsInWebContent</key>
<true/>
</dict>
3. HTMLの表示
HTMLを表示するコードは、次のとおりです。
import UIKit
import WebKit
// ViewController
class ViewController: UIViewController {
// UI
var webView: WKWebView!
// ビューのロード時に呼ばれる
override func viewDidLoad() {
super.viewDidLoad()
// コンフィギュレーションの準備
let config = WKWebViewConfiguration()
// Webビューの生成
self.webView = WKWebView(frame:.zero, configuration: config)
self.view = self.webView
// HTMLの表示
self.load("https://www.google.com/")
}
// HTMLの表示
private func load(_ url: String) {
self.webView.load(URLRequest(url: URL(string: url)!))
}
}
4. ローカルHTMLの表示
ローカルHTMLを表示する手順は、次のとおりです。
(1) ローカルHTMLの準備。
htmlフォルダにindex.htmlを配置します。
・html
・index.html
(2) index.htmlを以下のように編集。
今回は、「これはテストです。」と表示するだけのHTMLです。
<!DOCTYPE html>
<html lang="ja">
<head>
<title>タイトル</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="format-detection" content="telephone=no">
</head>
<body>
これはテストです。
</body>
</html>
(3) htmlフォルダをXcodeのプロジェクトにドラッグ&ドロップし、「Create folder reference」を選択し、「Add to target」でターゲットが選択されてることを確認し、「Finish」ボタンを押す。
(4) ViewControllerのコードを、以下のように編集。
import UIKit
import WebKit
// ViewController
class ViewController: UIViewController {
// UI
var webView: WKWebView!
// ビューのロード時に呼ばれる
override func viewDidLoad() {
super.viewDidLoad()
// コンフィギュレーションの準備
let config = WKWebViewConfiguration()
// Webビューの生成
self.webView = WKWebView(frame:.zero, configuration: config)
self.view = self.webView
// ローカルHTMLの表示
self.loadLocalHtml("index.html")
}
// ローカルHTMLの表示
func loadLocalHtml(_ name: String) {
let url = Bundle.main.url(forResource: name, withExtension: "", subdirectory: "html")!
self.webView.loadFileURL(url, allowingReadAccessTo: url)
}
}
5. WKWebViewConfiguration
「WKWebViewConfiguration」でWebビューの各種設定を行うことができます。
主なプロパティは、次のとおりです。
◎ Webビューの設定
var websiteDataStore: WKWebsiteDataStore
キャッシュの設定。
・WKWebsiteDataStore.default() : キャッシュあり。
・WKWebsiteDataStore.nonPersistent() : キャッシュなし。
var userContentController: WKUserContentController
SwiftとJavaScriptの双方向通信の設定。
var processPool: WKProcessPool
セッションの設定。
var applicationNameForUserAgent: String?
ユーザーエージェントの設定。
var limitsNavigationsToAppBoundDomains: Bool
ナビゲーションをドメイン内のページに制限するかどうか。
var preferences: WKPreferences
Webビューのコンテンツの設定。
・minimumFontSize : 最小フォントサイズ (ポイント単位)
・tabFocusesLinks : Tabでフォーカスがリンクとフォームコントロールに変わるかどうか
・javaScriptCanOpenWindowsAutomatically : ユーザー操作なしにJavaScriptでウィンドウを開くことができるかどうか
・isFraudulentWebsiteWarningEnabled : 不正疑いのコンテンツに対する警告を表示するかどうか
◎ URLスキームの設定
func setURLSchemeHandler(WKURLSchemeHandler?, forURLScheme: String)
URLスキームに関連付けられたリソースを読み込むオブジェクトを登録。
func urlSchemeHandler(forURLScheme: String) -> WKURLSchemeHandler?
URLスキームに対して現在登録されているハンドラオブジェクトを返す。
◎ レンダリングの設定
var ignoresViewportScaleLimits: Bool
Webページのスケーリングを許可するかどうか。
var suppressesIncrementalRendering: Bool
コンテンツがメモリに完全にロードされるまで、Webビューがコンテンツのレンダリングを抑制するかどうか。
var allowsInlineMediaPlayback: Bool
HTML5ビデオをインラインで再生するかどうか。
var allowsAirPlayForMediaPlayback: Bool
AirPlayを介したメディア再生を許可するかどうか。
var allowsPictureInPictureMediaPlayback: Bool
HTML5ビデオがPictureinPictureを再生できるかどうか。
var mediaTypesRequiringUserActionForPlayback: WKAudiovisualMediaTypes
再生開始するためにユーザー操作を必要とするメディアタイプの取得。
・.audio : オーディオ
・.video : ビデオ
・.all : 全て
◎ データ検出器の設定
var dataDetectorTypes: WKDataDetectorTypes
コンテンツに適用するデータ検出器種別の取得。
・phoneNumber: テキスト内の電話番号を検出し、リンクに変換。
・link: テキスト内のURLを検出し、リンクに変換。
・address: テキスト内の住所を検出し、リンクに変換。
・calendarEvent: テキスト内の日付と時刻を検出し、リンクに変換。
・trackingNumber: テキスト内の追跡番号を検出し、リンクに変換。
・flightNumber: テキスト内のフライト番号を検出し、リンクに変換。
・lookupSuggestion: Spotlightの提案を検出し、リンクに変換。
・all: すべてのデータ型を検出し、リンクに変換。
◎ コンテンツの選択粒度の設定
var selectionGranularity: WKSelectionGranularity
コンテンツの選択粒度の取得。
◎ ユーザーインターフェイスの方向の設定
var userInterfaceDirectionPolicy: WKUserInterfaceDirectionPolicy
ユーザーインターフェイスの方向の取得。
・content: CSS/HTML/XHTML仕様に従う。
・system: ビューのユーザーインターフェイスレイアウトに従う。
「キャッシュなし」と「ユーザーエージェント」を設定するコードは、次のとおりです。
import UIKit
import WebKit
// ViewController
class ViewController: UIViewController {
// UI
var webView: WKWebView!
// ビューのロード時に呼ばれる
override func viewDidLoad() {
super.viewDidLoad()
// コンフィギュレーションの準備
let config = WKWebViewConfiguration()
config.websiteDataStore = WKWebsiteDataStore.nonPersistent() // キャッシュなし
config.applicationNameForUserAgent = "CustomUserAgent" // ユーザーエージェント
// Webビューの生成
self.webView = WKWebView(frame:.zero, configuration: config)
self.view = self.webView
// HTMLの表示
self.load("https://www.google.com/")
}
// HTMLの表示
private func load(_ url: String) {
self.webView.load(URLRequest(url: URL(string: url)!))
}
}
6. WKUIDelegate
「WKUIDelegate」は、WebビューのUIを制御するデリゲートです。主にウィンドウ、ダイアログ(alert, confirm, prompt)、コンテキストメニューの3つを制御します。ウィンドウ生成時には、コンフィギュレーションの設定を行うこともできます。
主なメソッドは、次のとおりです。
◎ ウィンドウの制御
func webView(WKWebView, createWebViewWith: WKWebViewConfiguration, for: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView?
ウィンドウのオープン時に呼ばれる。
func webViewDidClose(WKWebView)
ウィンドウのクローズ時に呼ばれる。
◎ ダイアログの制御
func webView(WKWebView, runJavaScriptAlertPanelWithMessage: String, initiatedByFrame: WKFrameInfo, completionHandler: () -> Void)
alertダイアログの表示時に呼ばれる。
func webView(WKWebView, runJavaScriptConfirmPanelWithMessage: String, initiatedByFrame: WKFrameInfo, completionHandler: (Bool) -> Void)
confirmダイアログの表示時に呼ばれる。
func webView(WKWebView, runJavaScriptTextInputPanelWithPrompt: String, defaultText: String?, initiatedByFrame: WKFrameInfo, completionHandler: (String?) -> Void)
promptダイアログの表示時に呼ばれる。
func webView(WKWebView, runOpenPanelWith: WKOpenPanelParameters, initiatedByFrame: WKFrameInfo, completionHandler: ([URL]?) -> Void)
ファイルアップロードダイアログの表示時に呼ばれる。
◎ コンテキストメニューの表示
func webView(WKWebView, contextMenuConfigurationForElement: WKContextMenuElementInfo, completionHandler: (UIContextMenuConfiguration?) -> Void)
コンテキストメニューの開始時に呼ばれる
func webView(WKWebView, contextMenuForElement: WKContextMenuElementInfo, willCommitWithAnimator: UIContextMenuInteractionCommitAnimating)
コンテキストメニューのアニメーターの提供時に呼ばれる。
func webView(WKWebView, contextMenuWillPresentForElement: WKContextMenuElementInfo)
コンテキストメニューのオープン時に呼ばれる。
func webView(WKWebView, contextMenuDidEndForElement: WKContextMenuElementInfo)
コンテキストメニューのクローズ時に呼ばれる。
alertダイアログを表示するコードは、次のとおりです。
import UIKit
import WebKit
// ViewController
class ViewController: UIViewController, WKUIDelegate {
// UI
var webView: WKWebView!
// ビューのロード時に呼ばれる
override func viewDidLoad() {
super.viewDidLoad()
// コンフィギュレーションの準備
let config = WKWebViewConfiguration()
// Webビューの生成
self.webView = WKWebView(frame:.zero, configuration: config)
self.view = self.webView
// UIデリゲートの指定
self.webView.uiDelegate = self
// HTMLの表示
self.load("https://www.google.com/")
}
// Webページの表示
private func load(_ url: String) {
self.webView.load(URLRequest(url: URL(string: url)!))
}
// 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)
}
}
7. WKNavigationDelegate
「WKNavigationDelegate」は、Webビューのコンテンツの読み込み状況を把握するためのデリゲートです。
主なメソッドは、次のとおりです。
◎ リクエストとレスポンスの許可
func webView(WKWebView, decidePolicyFor: WKNavigationAction, decisionHandler: (WKNavigationActionPolicy) -> Void)
リクエストを許可するか判定。
・.allow : 許可
・.cancel : 拒否
func webView(WKWebView, decidePolicyFor: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void)
レスポンスを許可するか判定。
・.allow : 許可
・.cancel : 拒否
func webView(WKWebView, didReceive: URLAuthenticationChallenge, completionHandler: (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
ユーザー認証の実行。
・.useCredential : 許可。
・.performDefaultHandling : 未実装。
・.cancelAuthenticationChallenge : キャンセル。
・.rejectProtectionSpace : リジェクト。
◎ コンテンツの読み込み状況
func webView(WKWebView, didStartProvisionalNavigation: WKNavigation!)
コンテンツの読み込みの準備。
func webView(WKWebView, didCommit: WKNavigation!)
コンテンツの読み込みの開始。
func webView(WKWebView, didFinish: WKNavigation!)
コンテンツの読み込みの完了。
func webView(WKWebView, didFailProvisionalNavigation: WKNavigation!, withError: Error)
HTML読み込み開始時でのエラー発生時(通信圏外など)に呼ばれる。
func webView(WKWebView, didFail: WKNavigation!, withError: Error)
HTML読み込み途中でのエラー発生時(HTML読み込み中のキャンセルなど)に呼ばれる。
func webView(WKWebView, didReceiveServerRedirectForProvisionalNavigation: WKNavigation!)
リダイレクト時に呼ばれる。
コンテンツの読み込み状況をログ出力するコードは、次のとおりです。
import UIKit
import WebKit
// ViewController
class ViewController: UIViewController, WKNavigationDelegate {
// UI
var webView: WKWebView!
// ビューのロード時に呼ばれる
override func viewDidLoad() {
super.viewDidLoad()
// コンフィギュレーションの準備
let config = WKWebViewConfiguration()
// Webビューの生成
self.webView = WKWebView(frame:.zero, configuration: config)
self.view = self.webView
// ナビゲーションデリゲートの指定
self.webView.navigationDelegate = self
// Webページの表示
self.load("https://www.google.com/")
}
// Webページの表示
private func load(_ url: String) {
self.webView.load(URLRequest(url: URL(string: url)!))
}
// リクエストを許可するか判定
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
print("リクエストを許可するか判定")
// リクエストURLの取得
let url = navigationAction.request.url
print(url!)
// リクエストの許可(許可:.allow, 拒否:.cancel)
decisionHandler(.allow)
}
// レスポンスを許可するか判定
func webView(_ webView: WKWebView,
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
print("レスポンスを許可するか判定")
//レスポンスの許可(許可:.allow, 拒否:.cancel)
decisionHandler(.allow)
}
// ユーザー認証の実行
func webView(_ webView: WKWebView,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {
print("ユーザー認証の実行")
// ユーザー認証の許可(許可:.useCredential, 未実装:.performDefaultHandling,
// キャンセル:.cancelAuthenticationChallenge, 拒否:.rejectProtectionSpace)
completionHandler(.useCredential, nil)
}
// コンテンツの読み込みの準備
func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
print("コンテンツの読み込みの準備")
}
// コンテンツの読み込みの開始
func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
print("コンテンツの読み込みの開始")
}
// コンテンツの読み込みの完了
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
print("コンテンツの読み込みの完了")
}
// エラー時に呼ばれる
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError: Error) {
print("エラー時に呼ばれる")
}
// エラー時に呼ばれる
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError: Error) {
print("エラー時に呼ばれる")
}
// リダイレクト時に呼ばれる
func webView(_ webView: WKWebView, didReceiveServerRedirectForProvisionalNavigation:WKNavigation!) {
print("リダイレクト時に呼ばれる")
}
}
リクエストを許可するか判定
https://www.google.com/
コンテンツの読み込みの準備
ユーザー認証の実行
レスポンスを許可するか判定
コンテンツの読み込みの開始
コンテンツの読み込みの完了
ユーザー認証の実行
ユーザー認証の実行
8. SwiftからのJavaScriptの操作
SwiftからJavaScriptを操作するには、Swift側でevaluateJavaScript()を使います。
(1) index.htmlを以下のように編集。
<!DOCTYPE html>
<html lang="ja">
<head>
<title>タイトル</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="format-detection" content="telephone=no">
</head>
<body>
これはテストです。<br>
<p id="output"></p>
</body>
</html>
(2) ViewControllerのコードを、以下のように編集。
import UIKit
import WebKit
// ViewController
class ViewController: UIViewController, WKNavigationDelegate {
// UI
var webView: WKWebView!
// ビューのロード時に呼ばれる
override func viewDidLoad() {
super.viewDidLoad()
// コンフィギュレーションの準備
let config = WKWebViewConfiguration()
// Webビューの生成
self.webView = WKWebView(frame:.zero, configuration: config)
self.view = self.webView
// ナビゲーションデリゲートの指定
self.webView.navigationDelegate = self
// ローカルHTMLの表示
self.loadLocalHtml("index.html")
}
// ローカルHTMLの表示
func loadLocalHtml(_ name: String) {
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "html")!
webView.loadFileURL(url, allowingReadAccessTo: url)
}
// コンテンツの読み込みの完了時に呼ばれる
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// SwiftからJavaScriptを操作
let script = "document.getElementById('output').innerHTML = 'SwiftからのJavaScriptの操作。'"
self.webView.evaluateJavaScript(script, completionHandler: nil)
}
}
9. JavaScriptからのSwiftの操作
JavaScriptからSwiftを操作するには、JavaScript側でwindow.webkit.messageHandlers.XXXX.postMessage()を使います。
Swift側はコンテンツコントローラで「WKScriptMessageHandler」を実装します。
(1) index.htmlを以下のように編集。
<!DOCTYPE html>
<html lang="ja">
<head>
<title>タイトル</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta name="format-detection" content="telephone=no">
</head>
<body>
これはテストです。
<script>
window.webkit.messageHandlers.test.postMessage("JavaScriptからSwiftの操作")
</script>
</body>
</html>
(2) ViewControllerのコードを、以下のように編集。
import UIKit
import WebKit
// ViewController
class ViewController: UIViewController, WKScriptMessageHandler {
// UI
var webView: WKWebView!
// ビューのロード時に呼ばれる
override func viewDidLoad() {
super.viewDidLoad()
// コンフィギュレーションの準備
let config = WKWebViewConfiguration()
let userContentController: WKUserContentController = WKUserContentController()
userContentController.add(self, name: "test") // コンテンツコントローラの追加
config.userContentController = userContentController
// Webビューの生成
self.webView = WKWebView(frame:.zero, configuration: config)
self.view = self.webView
// ローカルHTMLの表示
self.loadLocalHtml("index.html")
}
// ローカルHTMLの表示
func loadLocalHtml(_ name: String) {
let url = Bundle.main.url(forResource: "index", withExtension: "html", subdirectory: "html")!
webView.loadFileURL(url, allowingReadAccessTo: url)
}
// JavaScriptからSwiftを操作
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if (message.name == "test") {
print(message.body)
}
}
}
10. AutoLayoutの設定
これまで、WKWebViewをViewControllerのルートとして使用してきました。今回は、任意のビューに追加し、AutoLayoutの設定を行い、画面サイズに応じてリサイズするように設定します。
AutoLayoutの設定の手順は、次のとおりです。
(1) translatesAutoresizingMaskIntoConstraintsにfalseを指定。
AutoLayout以前に使われていた、Autosizingのレイアウトの仕組みをAutoLayoutに変換するかどうかのフラグです。
self.webView.translatesAutoresizingMaskIntoConstraints = false
(2) 親ビューと子ビューのフレームを一致させる。
self.webView.frame = self.view.frame
(3) 4方向(top, bottom , leading, tailing)が等しくなる制約を追加。
self.webView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.webView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
コードは、次のとおりです。
import UIKit
import WebKit
// ViewController
class ViewController: UIViewController {
// UI
var webView: WKWebView!
// ビューのロード時に呼ばれる
override func viewDidLoad() {
super.viewDidLoad()
// コンフィギュレーションの準備
let config = WKWebViewConfiguration()
// Webビューの生成
self.webView = WKWebView(frame:.zero, configuration: config)
self.view.addSubview(self.webView)
// AutoLayoutの設定
self.webView.translatesAutoresizingMaskIntoConstraints = false
self.webView.frame = self.view.frame
self.webView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
self.webView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
self.webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
self.webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
// HTMLの表示
self.load("https://www.google.com/")
}
// HTMLの表示
private func load(_ url: String) {
self.webView.load(URLRequest(url: URL(string: url)!))
}
}
11. Webビューのキャッシュ削除
◎ Webビューの全キャッシュ削除
// Webビューの全キャッシュ削除
func removeWebViewCache(_ completion: (() -> Void)!) {
WKWebsiteDataStore.default().removeData(
ofTypes: WKWebsiteDataStore.allWebsiteDataTypes(),
modifiedSince: Date(timeIntervalSince1970: 0),
completionHandler: completion)
}
◎ Webビューの個別キャッシュ削除
// Webビューの個別キャッシュ削除
func removeWebViewCache(_ completion: (() -> Void)!) {
let types: Set<String> = [
WKWebsiteDataTypeCookies,
WKWebsiteDataTypeMemoryCache,
WKWebsiteDataTypeDiskCache,
WKWebsiteDataTypeOfflineWebApplicationCache,
WKWebsiteDataTypeLocalStorage,
WKWebsiteDataTypeSessionStorage,
WKWebsiteDataTypeWebSQLDatabases,
WKWebsiteDataTypeIndexedDBDatabases]
WKWebsiteDataStore.default().removeData(
ofTypes: types,
modifiedSince: Date(timeIntervalSince1970: 0),
completionHandler: completion)
}
◎ クッキー
・WKWebsiteDataTypeCookies : クッキー
◎ キャッシュ
・WKWebsiteDataTypeMemoryCache : インメモリキャッシュ
・WKWebsiteDataTypeDiskCache : オンディスクキャッシュ
・WKWebsiteDataTypeOfflineWebApplicationCache : HTMLオフラインWebアプリのキャッシュ
◎ ストレージ
・WKWebsiteDataTypeLocalStorage : HTMLローカルストレージ
・WKWebsiteDataTypeSessionStorage : HTMLセッションストレージ
◎ データベース
・WKWebsiteDataTypeWebSQLDatabases : WebSQLデータベース
・WKWebsiteDataTypeIndexedDBDatabases : データベース
12. カスタムWebビュー
よく使うする機能を追加したカスタムWebビューの例は、次のとおりです。
・loadHtml(_ url: String) - HTMLの読み込み
・loadLocalHtml(_ url: String) - ローカルHTMLの読み込み
・alert()
・confirm()
・prompt()
・console.log() - JavaScriptコンソール
・window.webkit.messageHandlers.event.postMessage(str) - 汎用イベント
・通信エラーダイアログ
・WebView.swift
import WebKit
// カスタムWebビュー
class WebView : WKWebView, WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler {
public var eventDelegate: ((String) -> Void)! // イベントデリゲート
// 初期化
init(frame: CGRect, configuration: WKWebViewConfiguration, useRedirectCookieHandling: Bool = false) {
super.init(frame: frame, configuration: configuration)
// JavaScriptコンソール
configuration.userContentController.add(self, name: "logging")
let _override = WKUserScript(
source: "var console = { log: function(msg){window.webkit.messageHandlers.logging.postMessage(msg) }};",
injectionTime: .atDocumentStart, forMainFrameOnly: true)
configuration.userContentController.addUserScript(_override)
// 汎用イベント
configuration.userContentController.add(self, name: "event")
// デリゲート
self.navigationDelegate = self
self.uiDelegate = self
}
// 初期化
required init?(coder: NSCoder) {
super.init(coder: coder)
}
// JavaScriptからSwiftを操作
public func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
// JavaScriptコンソール
if message.name == "logging" {
print(message.body)
}
// 汎用イベント
else if message.name == "event" {
if self.eventDelegate != nil {
self.eventDelegate(message.body as! String)
}
}
}
// HTMLの読み込み
func loadHtml(_ url: String) {
self.load(URLRequest(url: URL(string: url)!))
}
// ローカルHTMLの読み込み
func loadLocalHtml(_ name: String) {
let url = Bundle.main.url(forResource: name, withExtension: "", subdirectory: "html")!
self.loadFileURL(url, allowingReadAccessTo: url)
}
// 親のビューコントローラの取得
func parentViewController() -> UIViewController? {
var parentResponder: UIResponder? = self
while true {
guard let nextResponder = parentResponder?.next else {return nil}
if let viewController = nextResponder as? UIViewController {
return viewController
}
parentResponder = nextResponder
}
}
// 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()
})
self.parentViewController()?.present(alertController, animated: true, completion: nil)
}
// confirmダイアログの表示時に呼ばれる
func webView(_ webView: WKWebView,
runJavaScriptConfirmPanelWithMessage message: String,
initiatedByFrame frame: WKFrameInfo,
completionHandler: @escaping (Bool) -> Void) {
let alertController = UIAlertController(
title: "", message: message, preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "キャンセル", style: .cancel) {action in
completionHandler(false)
})
alertController.addAction(UIAlertAction(title: "OK", style: .default) {action in
completionHandler(true)
})
self.parentViewController()?.present(alertController, animated: true, completion: nil)
}
// promptダイアログの表示時に呼ばれる
func webView(_ webView: WKWebView,
runJavaScriptTextInputPanelWithPrompt prompt: String,
defaultText: String?,
initiatedByFrame frame: WKFrameInfo,
completionHandler: @escaping (String?) -> Void) {
let alertController = UIAlertController(
title: "", message: prompt, preferredStyle: .alert)
alertController.addTextField() {$0.text = defaultText}
alertController.addAction(UIAlertAction(title: "キャンセル", style: .cancel) {action in
completionHandler("")
})
alertController.addAction(UIAlertAction(title: "OK", style: .default) {action in
if let textField = alertController.textFields?.first {
completionHandler(textField.text)
} else {
completionHandler("")
}
})
self.parentViewController()?.present(alertController, animated: true, completion: nil)
}
// エラー時に呼ばれる
func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError: Error) {
print(withError.localizedDescription)
showError()
}
// エラー時に呼ばれる
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError: Error) {
print(withError.localizedDescription)
showError()
}
// エラーの表示
func showError() {
let alertController = UIAlertController(
title: "", message: "通信失敗しました。", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "OK", style: .default) {action in
})
self.parentViewController()?.present(alertController, animated: true, completion: nil)
}
}
・テスト用HTML
<!DOCTYPE html>
<html lang="ja">
<head>
<title>タイトル</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
<meta name="format-detection" content="telephone=no">
</head>
<body>
カスタムWebビューのテストです。<br>
<input type="button" value="alert" onclick="alert('alertのテストです。')"><br>
<input type="button" value="confirm" onclick="r=confirm('confirmのテストです。');alert(r)"><br>
<input type="button" value="prompt" onclick="r=prompt('promptのテストです。');alert(r)"><br>
<input type="button" value="console.log" onclick="console.log('console.log()のテストです。')"><br>
<input type="button" value="event" onclick="window.webkit.messageHandlers.event.postMessage('eventのテストです。')"><br>
</body>
</html>
次回
この記事が気に入ったらサポートをしてみませんか?