何が問題か?
WebView内のコンテンツがSPA(Single Page Application)で作られていると、ページ遷移しても期待したようにWKWebViewのdelegateが呼ばれない場合があります。SPAは最初にhtmlを取得したらJavaScriptによって画面の書き換えを行うので、webページの読み込みが初回のみしか発生しないためです。
例えばwebコンテンツがページ遷移するたび、htmlのtitleタグの情報を取得しnavigationItem.titleにセットする場合を考えます。
普通のwebページなら読み込み完了のタイミングで以下のようにtitleをセットするので問題ありませんが、SPAだとこのdelegateは初回のみしか呼ばれません。そのため最初にセットされたtitleがnavigationItemに残り続け、遷移先のtitleはnavigationItemに反映されません。
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
guard let title = webView.title else { return }
navigationItem.title = title
}
どうするか?
今回の例では以下のふたつを組み合わせて対応できます。
-
viewDidLayoutSubviews内でurlの変更を監視する - SwiftからJavaScriptを実行して
titleを取得する
class ViewController: UIViewController {
var webView: WKWebView!
private var urlObserver: NSKeyValueObservation?
// SPAのwebページ遷移で呼ばれる
override func viewDidLayoutSubviews() {
urlObserver = webView.observe(\.url, options: .new) { [weak self] _, _ in
guard let self = self else { return }
self.setTitle()
}
}
private func setTitle() {
// jsのdocument.titleプロパティを実行
let script = "document.title"
webView.evaluateJavaScript(script) { (result, _) in
self.navigationItem.title = result as? String
}
}
}
extension ViewController: WKNavigationDelegate {
// 初回のWebView読み込み時によばれる
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
setTitle()
}
}
以上のようにtitleを取得できますし、同じ要領でurlも取得可能です。
他の場面においてもSPAで構成されたwebコンテンツでWKWebViewのdelegateを使用したい場合は、適宜urlの監視をうまく使ってやるのが有効かもしれません。