Help us understand the problem. What is going on with this article?

【swift4】WKWebViewリファレンス日本語訳

公式ドキュメントをすごく軽く訳したものを少し加筆しつつ、書いていきます。
公式ドキュメントは以下から。

https://developer.apple.com/documentation/webkit/wkwebview

WKWebView

WKWebViewオブジェクトは、アプリ内ブラウザなどのインタラクティブなWebコンテンツを表示することができます。 WKWebViewクラスを使用して、Webコンテンツをアプリケーションに埋め込むことができます。これを行うには、WKWebViewオブジェクトをコードから作成し、それをビューとして設定し、Webコンテンツのロードを行います。

つまり、アプリ内でWebページを扱う場合、とりあえず、これを使えって感じですね。現在WKWebViewはストーリーボードで生成できないので、コードから生成してViewを適用する形になります。

概要

Important
iOS8.0およびOS X 10.10以降、WKWebViewを使用してください。 UIWebViewまたはWebViewは使用しないでください。

UIWebViewなどは非推奨なようです。

init(frame:configuration:) メソッドで、新しいWKWebViewオブジェクトを作成した後、Webコンテンツを読み込むことができます。
loadHTMLString(_:baseURL :) メソッドを使用してローカルのHTMLファイルの読み込みを開始するか、またはload(_:)メソッドを呼び出してWebコンテンツの読み込みを開始します。
読み込みを停止するにはstopLoading()メソッドを使用し、ロード中はisLoadingプロパティを使用してWebビューが読み込み中かどうかを調べルことができます。
デリゲートプロパティをWKUIDelegateプロトコルに準拠するオブジェクトに設定して、Webコンテンツの読み込みを追跡します。プログラムでWKWebViewを作成する例は、リスト1を参照してください。

web上のコンテンツだけでなく、ローカルのHTMLも読み込むことができるんですね。
また、読み込みの監視や、読み込みの停止も行えるようです。
リスト1というのは、以下のサンプルコードになります。

Listing1
import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate {

    var webView: WKWebView!

    override func loadView() {
        let webConfiguration = WKWebViewConfiguration()
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        view = webView
    }
    override func viewDidLoad() {
        super.viewDidLoad()

        let myURL = URL(string: "https://www.apple.com")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)
    }}

実行してみました。動作は以下のようです。
wktest.gif

ユーザがWebページの履歴の「戻る」「進む」を行うには、goBack()goForward()をボタンのアクション内で使ってください。
また、「戻る」「進む」ができない場合(最初のページや最新のページ)では該当のボタンを非表示にするなどしたい時、それぞれcanGoBackcanGoForwardで監視することができます。

デフォルトでは、WebビューはWebコンテンツに表示される電話番号を電話リンクに自動的に変換します。電話リンクがタップされると、電話アプリが起動してその番号にダイヤルします。この設定をオフにするには、dataDetectorTypesプロパティをWKDataDetectorTypesphoneNumberフラグを含まないように設定してください。

また、Webビューに最初に表示されたときにWebコンテンツの倍率をsetMagnification(_:centeredAt:)で設定することができます。その後、ユーザはジェスチャを用いて倍率を変更することができます。

デフォルトの設定を色々いじれるみたいですね。

WKWebViewのメソッド、プロパティ

WebKitがコンテンツをロードできるかどうかの判定

class func handlesURLScheme(_ urlScheme: String) -> Bool

WebKitがネイティブで特定のURLスキームでリソースを読み込むことをサポートしているかどうかを返します。

WebViewの初期化

var configuration: WKWebViewConfiguration

Webビューを初期化するための設定のコピー。

init(frame: CGRect, configuration: WKWebViewConfiguration)

指定されたframeと設定で初期化されたWebビューを返します。

init?(coder: NSCoder)

概要なし

生成の流れとしては、以下のようになるかと思います。

let webConfiguration = WKWebViewConfiguration()
webView = WKWebView(frame: .zero, configuration: webConfiguration)

WKWebViewのプロパティ

var scrollView: UIScrollView

Webビューに関連したスクロールビューです。
ここにデリゲートして、下にスクロールしたか、上にスクロールしたか、などを検知することができます。

TwitterのWebViewとかは、これを検知して、下にスクロールした場合は、ヘッダーとフッターを収納、上にスクロールした場合は展開しています。つまり、閲覧時の表示領域を大きくとることでユーザエクスペリエンスの向上を狙っているんですね。

var title: String?

ページタイトル

var url: URL?

アクティブなURL

var customUserAgent: String?

ユーザーエージェントの名前。SafariとかiPhone7とか。

var serverTrust: SecTrust?

現在コミットされているナビゲーションのSecTrustRefオブジェクト。

var certificateChain: [Any]

Deprecated。
現在コミットされているナビゲーションの証明書チェーンを構成するオブジェクトの配列。

Delegateの設定

var navigationDelegate: WKNavigationDelegate?

webビューのナビゲーションのデリゲート。

  • 遷移開始時
  • ページの読み込み開始時
  • ページの読み込み完了時
  • 読み込みエラー発生時
  • リダイレクト時

などのイベントを受け取りたい場合はこのDelegateを継承します。
以下、実装例。

import UIKit
import WebKit
class ViewController: UIViewController, WKUIDelegate ,WKNavigationDelegate{

    var webView: WKWebView!

    override func loadView() {
        let webConfiguration = WKWebViewConfiguration()
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        webView.navigationDelegate = self
        view = webView
    }
    override func viewDidLoad() {
        super.viewDidLoad()

        let myURL = URL(string: "https://www.apple.com")
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)
    }}

    func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
        print("遷移開始")
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        print("読み込み完了")
        print(webView.title as Any)
    }
}

var uiDelegate: WKUIDelegate?

webビューのユーザインターフェースのデリゲート。
これはとりあえず継承しておくやつです。注意としては、

webView.delegate = self

ではなく、

webView.uiDelegate = self

とすること。stackoverflowで「あれもこれもやったのに、全然表示されない!」と悩んでるコードが上記のミスを犯していて、真顔で指摘されていました。

コンテンツの読み込み

var estimatedProgress: Double

現在のナビゲーションで、どれだけ読み込まれたかの推定値。0.0-1.0の間をとります。

var hasOnlySecureContent: Bool

ページ上のすべてのリソースが安全に暗号化された接続を介してロードされているかどうかを示すブール値。

func loadHTMLString(String, baseURL: URL?)

WebページのコンテンツとベースURLを設定します。

var isLoading: Bool

ビューが現在コンテンツをロードしているかどうかを示すブール値。

func reload()

現在のページを再読み込みします。

func reloadFromOrigin()

可能であれば、キャッシュの有効性の問い合わせ(cache-validating conditionals)を行い。エンドツーエンドの再検証(end-to-end revalidation)を行い、現在のページをリロードします。

よくわかりません。

func stopLoading()

現在のページの全てのリソースの読み込みを停止します。

func load(Data, mimeType: String, characterEncodingName: String, baseURL: URL)

WebページのコンテンツとベースURLを設定します。

func loadFileURL(URL, allowingReadAccessTo: URL)

ファイルシステム上の要求されたファイルのURLに移動します。
例えば、ローカルファイルを見やすい形で閲覧したい時に使えます。
Xlsxファイルとかいけます。

# 例えば、xcode上で追加したファイルを開いてみる。
let path: String = Bundle.main.path(forResource: "hogehoge", ofType: "xlsx")!
let url = URL(fileURLWithPath: path)
webView.loadFileURL(url, allowingReadAccessTo: url)

コンテンツの拡大縮小

var allowsMagnification: Bool

拡大ジェスチャがWebビューの倍率を変更するかどうかを示すブール値。
おそらく、最大倍率や最小倍率でさらに拡大や縮小しようとする時falseになるんじゃないでしょうか。

var magnification: CGFloat

現在のページの倍率。

func setMagnification(CGFloat, centeredAt: CGPoint)

指定された倍率でページを拡大縮小する。その際、centeredAtで指定された座標を中央に配置する。

Webページの遷移

var allowsBackForwardNavigationGestures: Bool

水平方向のスワイプジェスチャーがback-forwardのナビゲーションをトリガーするかどうかのブール値。

var backForwardList: WKBackForwardList

Webビューの back-forward リスト。進むや戻るでの遷移先の候補がリストで入っているのでしょう。
履歴という感覚で大丈夫だと思います。

var canGoBack: Bool

back-forward リストにナビゲートできるバックアイテムがあるかどうかを示すブール値。
つまり、戻れるかどうかを示します。

var canGoForward: Bool

back-forward リストにナビゲートできるフォワードアイテムがあるかどうかを示すブール値。
つまり、進めるかどうかを示します。

var allowsLinkPreview: Bool

3D Touchでリンクを押すと、リンク先のプレビューが表示されるかどうかを決定するブール値。
最後まで強く押すとWKWebViewではなくSafariで立ち上がるようです。

func goBack()

ページを戻る。back-forward リストから選ぶ。

ここで注意なのが、このメソッドを呼び出し、遷移が完了した時、必ずしもdidFinishLoadのイベントメソッドが呼び出されるわけではないということ。

webViewDidFinishLoad is not fired when I use goBack

WebViewは何ページ分かのページ情報のキャッシュを持っており、読み込みを行うかどうかはキャッシュ次第になってしまいます。なので、goBackなどを行う時は、必ずしもdidFinishメソッドが呼び出される訳ではないことに注意してコーディングする必要があります。

func goForward()

ページを進む。back-forward リストから選ぶ。

func go(to: WKBackForwardListItem)

back-forward リストから項目に移動し、現在の項目として設定します。
履歴内の特定のページに遷移できるようですね。

func load(URLRequest)

指定のURLから遷移する。引数がURLRequestなので、以下のようにURLRequestを生成する必要があるでしょう。

let myURL = URL(string: "https://www.apple.com")
let myRequest = URLRequest(url: myURL!)
webView.load(myRequest)

JavaScriptの実行

func evaluateJavaScript(String, completionHandler: ((Any?, Error?) -> Void)? = nil)

JavaScriptをStringで与えれば、実行してくれるようです。変態的なことをしようと思ったら、ここを弄る必要があるでしょう。

例として、userAgentを取得してみます。

webView.evaluateJavaScript("navigator.userAgent", completionHandler: {(res,err) in
            let res = res as! String
            print(res)
        })

# 実行結果
# Mozilla/5.0 (iPhone; CPU iPhone OS 11_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15F79

スナップショットを撮る

func takeSnapshot(with: WKSnapshotConfiguration?, completionHandler: (UIImage?, Error?) -> Void)

スクリーンショットが撮れるみたいですね。contextでレンダリングする必要がなくなりました。
(2018/04/21 追記)
スナップショット試しました。Webページの読み込みが終わった段階で表示させたかったので、WKNavigationDelegateのwebView(_ webView: WKWebView, didFinish navigation: WKNavigation!)で呼び出します。
画像は以下のように取得できます。

webView.takeSnapshot(with: nil) { (image, error) in
                self.imageView.image = image
            }

ただ、ここで取得したimageが真っ白になる問題が発生。
どうやら、didFinishメソッドは読み込み終了時に呼び出されてはいるんですが、その読み込んだコンテンツが描画されるまでには、もう少し時間がかかるみたいです。これは実験的に得た知見なので根拠はありません。

なので、didFinishで呼び出す場合には、遅延実行をしてあげましょう。僕はこれでうまく行きました。

DispatchQueue.main.asyncAfter(deadline: .now() + 1.0){
    webView.takeSnapshot(with: nil) { (image, error) in
        self.imageView.image = image
    }
}

余談系

userAgentの取得、変更

取得は以下のよう。

webView.evaluateJavaScript("navigator.userAgent", completionHandler: {(res,err) in
            let res = res as! String
            print(res)
        })

# 実行結果
# Mozilla/5.0 (iPhone; CPU iPhone OS 11_4 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15F79

変更は結構難しいみたいで、以下のようです。 参考

AppDelegate.swift
# didFinishLaunchingWithOptions内に記述
NSUserDefaults.standardUserDefaults().registerDefaults(["UserAgent" : "Custom Agent"])
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away