「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のタイトルとして表示する
これだけです。
(また下が黒い現象が・・・もう気にしない)
実現方法
まず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)
}
}
}
以上!
コード全体は以下に挙がっています。
前回と似た内容だったので、少し手抜き感あるかもですが、まあそういうこともある!(誰向け?笑)