LoginSignup
1
1

More than 1 year has passed since last update.

「The Ultimate Guide to WKWebView」をSwiftUIで実装する #04 - Loading HTML fragments -

Last updated at Posted at 2022-02-06

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

目次

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

# タイトル
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

実現したいこと

今回やることは
TextFieldに入力した文字列を元にHTML文字列を作成して表示します。

TextFieldになんも入力されてない時は、「nothing」の文字列が入るようにしました。
I love nothingってちょっと悲しい感じだけど・・・ :sweat:

またHTMLは、アプリバンドル内にあるCSSを読み込んでいます。
Hacking with Swiftの例より少しだけ発展版になっています。

今回の成果物は、こんな感じになりました。

gif

実現方法

まずWebViewです。

最初にinputTextを使って、HTML文字列を作成します。
次に、そのHTMLに適用するCSSをアプリバンドルから読み込み、loadHTMLString(_:baseURL:)baseURLに渡します。

ちなみにbaseURL

「HTML文字列の中にある、相対URLを解決する時に使用するURL」とのこと。

The base URL to use when the system resolves relative URLs within the HTML string.

今回のこのHTMLでは、<link rel="stylesheet" type="text/css" href="index.css">とあるように、CSSを適用しますので
その相対URLを取得してbaseURLに渡しているわけです。

WebView.swift
import Foundation
import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    let inputText: String

    func makeUIView(context: Context) -> WKWebView {
        return WKWebView()
    }

    func updateUIView(_ uiView: WKWebView, context: Context) {
        let displayedText = inputText.isEmpty ? "nothing" : inputText

        let html = """
        <html>
        <head>
        <link rel="stylesheet" type="text/css" href="index.css">
        </head>
        <body>
        <h1>Hello World!</h1>
        <h2>I am kamimi. I love \(displayedText).</h2>
        </body>
        </html>
        """
        
        guard let urlPath = Bundle.main.path(forResource: "index", ofType: "css") else {
            return
        }
        let url = NSURL.fileURL(withPath: urlPath)
        uiView.loadHTMLString(html, baseURL: url)
    }
}

ちなみに適用したCSSはこれ。
h1タグはオレンジに、h2タグは青になります。

index.css
h1 {
  color: orange;
  font-size: 100px;
}

h2 {
  color: blue;
  font-size: 80px;
}

Hacking with Swiftの例では、あまりbaseURLの使い方は説明されてなかったので、試しに使ってみました。

またその例では、このHTMLをただ表示しているだけだったのですが、

<html>
<body>
<h1>Hello, Swift!</h1>
</body>
</html>

loadHTMLString(_:baseURL:)のAppleのドキュメントを見たところ、Discussionに

「例えば、アプリがプログラムで生成したHTMLコンテンツを読み込むために使う」

For example, you might use this method to load HTML content that your app generates programmatically.

とあったので、TextFieldに入力された文字列をHTML文字列の中に埋め込む、というそれっぽい方法でやってみました。

ちなみに、仕事でこのメソッドを使用した時は、
APIのレスポンスの一部がこんな感じのHTML文字列だったので、それをWebViewで表示したいというユースケースでした。

{
  "content": "<html><body><h1>Hello, Swift!</h1></body></html>"
}

このHTML文字列の場合であれば、baseURLnilで良さそうですね。
特に相対パス埋め込まれていませんし。


では次に、WebViewを表示するViewです。

TextFieldが1つと、WebViewを開くためのボタンが1つあります。
開いたWebViewは、左上に閉じるボタンをつけました。

ContentView.swift
import SwiftUI

struct ContentView: View {
    @State private var isShownWebView = false
    @State private var inputText = ""

    var body: some View {
        VStack {
            TextField("", text: $inputText)
                .border(.yellow)
                .padding()
            Button(action: {
                isShownWebView.toggle()
            }) {
                Text("WebView開く")
            }
        }
        .fullScreenCover(isPresented: $isShownWebView) {
            webBaseView
        }
    }
}

private extension ContentView {
    var webBaseView: some View {
        NavigationView {
            WebView(inputText: inputText)
                .navigationBarItems(leading: closeButton)
        }
    }

    var closeButton: some View {
        Button(action: {
            isShownWebView.toggle()
        }) {
            Text("閉じる")
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

以上です。

コード全体はこちらにあげています。

どうでも良い個人的な話なのですが、
Swiftも書きながら、HTML、CSSも書いてーとか色々触るの楽しいですね。:blush:

参考

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