LoginSignup
1
1

More than 1 year has passed since last update.

「The Ultimate Guide to WKWebView」をSwiftUIで実装する #08 - Reading a web page’s title as it changes -

Last updated at Posted at 2022-02-20

「The Ultimate Guide to WKWebView」をSwiftUIで実装してみるの、
8つ目になります。

前回と結構似てるので、軽めにいきます。

目次

シリーズ化していこうと思うので、全体の目次を置いておきます。
リンクが貼られていないアジェンダは、記事作成中または未作成のものになります。

# タイトル
01 Making a web view fill the screen
(WebViewを画面に表示する)
02 Loading remote content
(リモートのコンテンツを読み込む)
03 Loading local content
(ローカルのコンテンツを読み込む)
04 Loading HTML fragments
(HTMLフラグメントの読み込み)
05 Controlling which sites can be visited
(訪問可能なサイトの制御)
06 Opening a link in the external browser
(外部ブラウザでリンクを開く)
07 Monitoring page loads
(ページの読み込みを監視する)
08 Reading a web page’s title as it changes
(Webページのタイトルの変化を読み取る)
09 Reading pages the user has visited
(ユーザーが閲覧したページを読み取る)
10 Injecting JavaScript into a page
(JavaScriptをページに注入する)
11 Reading and deleting cookies
(cookieの読み取りと削除)
12 Providing a custom user agent
(カスタムUser Agentを提供する)
13 Showing custom UI
(カスタムUIを表示する)
14 Snapshot part of the page
(ページの一部のスナップショットを撮る)
15 Detecting data
(データの探索)

環境

【Xcode】13.1
【Swift】5.5
【iOS】15.0
【macOS】Big Sur バージョン 11.4

実現したいこと

今回やることは
読み込んでいるページのタイトルを取得して、Navigation Barのタイトルとして表示する
これだけです。

app.gif

(また下が黒い現象が・・・もう気にしない)

実現方法

まずWebViewです。

import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    let url: URL
    @Binding var title: String

    private let webView = WKWebView()

    func makeUIView(context: Context) -> WKWebView {
        webView.navigationDelegate = context.coordinator
        let request = URLRequest(url: url)
        webView.load(request)
        return webView
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
    }

    func makeCoordinator() -> Coordinator {
        return Coordinator(self)
    }
}

extension WebView {
    class Coordinator: NSObject {
        var parent: WebView

        var observations: [NSKeyValueObservation?] = []

        init(_ parent: WebView) {
            self.parent = parent

            // タイトルが取得でき次第、NavigationBarのタイトルに反映する
            let titleObservation = parent.webView.observe(\.title, options: .new, changeHandler: { _, value in
                parent.title = (value.newValue ?? "") ?? ""
            })
            observations = [titleObservation]
        }
    }
}

今回は、前回の記事でも使ったKVOで、ページのタイトルを取得します。

前回KVOで監視していた値は、ページの読み込み率だったのですが、
今度はそれがページのタイトルになっただけです。

なのですが、実はタイトルの取得方法はもう一つあります。↓

// 読み込み完了時に呼ばれ、NavigationBarのタイトルに反映する
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
    parent.title = webView.title ?? ""
}

それがこのwebView(_:didFinish:)メソッドを使用した方法です。

「ナビゲーションが完了したことをDelegateに通知する」というものです。

Tells the delegate that navigation is complete.

KVOでは、タイトルの値を常に監視しているので、タイトルに変化があり次第、ナビゲーションバーのタイトルにその値が反映されます。

対して、webView(_:didFinish:)メソッドを使った方法では、
ページの読み込みが完了した時点で、タイトルを取得し、ナビゲーションバーのタイトルにその値を反映します。

これからも想定できるように、KVOの方が取得と反映が早いです。

KVOの方法とwebView(_:didFinish:)メソッドを使う方法の両方に、printを仕込んでどちらが早くタイトルの値を出力するか、5回ほど試したのですが、
5回中5回、KVOの方が早かったです。(体感1,2秒ほど早い)

まあ確かめるまでもなかったかもしれませんが。

いろんな記事を見ても、どっちの方法でもやられているので、どっちでもいいのかね。

ここはよくわからなかったのですが、
特に何かデメリットがなければ、より早く反映されるKVOの方でいいんじゃないかなーと思い
今回はそちらを採用しています。

あと、元記事のHacking with SwiftではKVOだったというのもあります。

また、titleだけに限った話ではありませんが、title以外にも
KVOでも、webView(_:didFinish:)メソッドでも取れる値はあります。
canGoBackとか。)

今回取得したかったタイトルは、正直どちらで取得してもそこまで大差なさそうなので、どっちでも良いかなと思った次第です。


次にWebView.swiftを使うViewの実装です。
今回はシンプルです。

import SwiftUI

struct ContentView: View {
    private let url = URL(string: "https://www.apple.com/")!
    @State private var title = ""

    var body: some View {
        NavigationView {
            WebView(url: url, title: $title)
                .navigationBarTitle(title, displayMode: .inline)
        }
    }
}

以上!

コード全体は以下に挙がっています。

前回と似た内容だったので、少し手抜き感あるかもですが、まあそういうこともある!(誰向け?笑)

参考

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1