カテゴリー: iOS

【Swift】WKWebViewでタップしたURLを取得する。

はじめに

こんにちはsuzukiです。

最近WKWebViewを新規で追加する機会がありました。
以前の実装からしばらく経っていたため、実装漏れもあり想定以上に時間をかけた為、備忘もかねてまとめて行きたいと思います。

今回の要件
・表示されている画面のナビゲーションにタイトルを追加
・ロングタップの禁止
・特定のリンクの場合はブラウザを起動して表示
上記の3点となります。

該当しない箇所はサクサク進めて行こうと思います。

WKWebViewについて

ご存知の方も多いかとは思いますが、WKWebViewについてまとめさせていただきます。

WKWebViewはアプリの内でwebの表示を行うため用意されているオブジェクトです。
UIWebViewのほぼ上位互換で最近ではStoryboardから追加が可能になりました。
(今回使おうとしたのですが、DeploymentTargetがiOS11以上でないと使えませんでした、、)
ローカルの動画の再生などにも利用可能です。

WKWebViewの基本実装

今回の画面構成は下記となります。※toolbarのボタンとtoolbarは関連付けを行います。
・Navigation
title
・WKWebView
・Toolbar
button
・indicator

コード

import UIKit
import WebKit

class ViewController: UIViewController , WKNavigationDelegate{
    //WKWebView
    var webView: WKWebView!    
    var urlString:String = "https://yahoo.co.jp"
    var activityIndicator: UIActivityIndicatorView!
    
    @IBOutlet weak var toolbar: UIToolbar!
    // 各種ボタン
    @IBOutlet weak var backBtn: UIBarButtonItem!
    @IBOutlet weak var nextBtn: UIBarButtonItem!
    @IBOutlet weak var refreshBtn: UIBarButtonItem!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        //WKWebViewの作成
        setWKWebView()
        //Indicatorの作成
        setActivityIndicate()
  
        backBtn.isEnabled = webView.canGoBack
        nextBtn.isEnabled = webView.canGoForward
        
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        guard let url = URL(string: urlString) else {
            return
        }
        let req = URLRequest(url: url, cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalCacheData, timeoutInterval: 100)
        self.webView.load(req)
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    func setWKWebView(){
        var frame = view.bounds
        frame.size.height =
            frame.height
            - UIApplication.shared.statusBarFrame.height
            - (navigationController?.navigationBar.frame.height ?? 0)
            - toolbar.frame.height  // ステータスバー、ナビゲーションバー、ツールバーを除いた高さを設定
        webView = WKWebView(frame:frame)
        self.view.addSubview(webView)        
        webView.navigationDelegate = self
        webView.translatesAutoresizingMaskIntoConstraints = false
        webView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
        webView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
        webView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0).isActive = true
        webView.bottomAnchor.constraint(equalTo: toolbar.topAnchor, constant: 0).isActive = true
    }

    //indicatorの設定
    func setActivityIndicate() {
        activityIndicator = UIActivityIndicatorView()
        activityIndicator.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
        activityIndicator.center = CGPoint(x: UIScreen.main.bounds.size.width/2, y: UIScreen.main.bounds.size.height/2)
        activityIndicator.hidesWhenStopped = true
        activityIndicator.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
        self.webView.addSubview(activityIndicator)
    }

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        decisionHandler(.allow)
    }

    //通信の開始
    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        activityIndicator.startAnimating()
    }
    
    //読み込み完了時に呼ばれる
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        activityIndicator.stopAnimating()
        backBtn.isEnabled = webView.canGoBack
        nextBtn.isEnabled = webView.canGoForward
    }
    
    @IBAction func tapBackBtn(_ sender: AnyObject) {
        // 前の画面に戻る
        self.webView.goBack()
    }
    
    @IBAction func tapNextBtn(_ sender: AnyObject) {
        // 次の画面へ進む
        self.webView.goForward()
    }
    
    @IBAction func tapRefreshBtn(_ sender: AnyObject) {
        // リロード
        self.webView.reload()
    }
}

ナビゲーションにタイトルを追加

下記の通信完了時に呼ばれるデリゲートで表示されている画面のタイトルを取得を行います。

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)
   //読み込み完了時に呼ばれる
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        //タイトルの取得
        let title = self.webView?.title
        //ナビゲーションにタイトルを設定
        navigationItem.title = title
        activityIndicator.stopAnimating()
        backBtn.isEnabled = webView.canGoBack
        nextBtn.isEnabled = webView.canGoForward
    }

こちらでページの読み込みが完了するとナビゲーションタイトルが更新される様になりました。

ロングタップの禁止

WKWebViewで表示されているLinkを押した時の動作がWKWebViewには設定されています。
・長押しでアクションシートを表示する。
・3Dタッチでsafariでリンク先を表示する。
上記が元から設定されているのですが、今回不要なため機能を制限します。

下記のコードをsetWKWebView()の関数の中で記述しましょう。

webView.allowsLinkPreview = false
webView.evaluateJavaScript("document.body.style.webkitTouchCallout='none';", completionHandle

読み込み完了時に呼ばれるデリゲートの中で

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!)

下記のコードも記述します。

webView.configuration.preferences.javaScriptEnabled = true

上記の設定で長押しと3Dタッチが機能しなくなりました。

特定のリンクの場合はブラウザを起動して表示

こちらに関しては
・webViewでリンクを押された事を取得
・押されたリンクのURLを取得
・取得したURLが対象のリンクか確認
・アプリからsafariを起動
上記ができれば実装完了です。

まずwebViewでリンクを押された事を取得するため下記のデリゲートを実装しましょう。

func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)

こちらのデリゲートはリンクがタップされた時に呼ばれリンクの内容によって読み込みを行うか許可と禁止の選択が行えます。
・decisionHandler(.allow)を呼び出す読み込みの許可
・decisionHandler(.cancel)を呼び出す読み込みの禁止
今回はデリゲート内で特定のリンクの場合はブラウザを起動して表示します。
下記のコードでは、特定のリンクの場合はwebViewはそのままの画面を表示するために読み込みの禁止し、該当しない場合は読み込みを許可しております。

    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {

        if let url =  navigationAction.request.url,
            //取得したURLが対象のリンクか確認を行う
            url == URL(string:"https://news.yahoo.co.jp/pickup/6292684"){
            //アプリからsafariを起動
            UIApplication.shared.openURL(url)
            decisionHandler(WKNavigationActionPolicy.cancel)
        } else {
            decisionHandler(WKNavigationActionPolicy.allow)
        }
    }

今回は特定のリンクでサファリを開きましたが、アプリ内の別画面に遷移などもこちらの箇所から設定できます。

さいごに

今回WKWebViewのコードのをまとめて思いましたが、
昔使用していたUIWebViewに比べ、設定する内容が多いので手間ですね。
早くiOS11以上の端末のみを対象にして、Storyboardでペタペタ配置したいです。

そろそろXCode10のベータ版も試していきたいと思います。
最後までありがとうございました。

— NORMAL —
suzuki

シェア
執筆者:
suzuki
タグ: Swift

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

2週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前