WKWebView の読み込み進捗を表示する

はじめに

  • iOS の WebView の読み込み進捗を示すプログレスバーを表示させたい
  • iOS 5 以降 / Objective-C の時代には
  • プログレス表示に NJKWebViewProgressView を使用していたが
  • WebKit の WKWebView(not UIWebView) と Swift のあたらしい KVO の仕組み(closure でスリムに書ける)を使うと
  • 以前より簡単に実装できるような感じがしたので
  • 実際に試してみる

資料

WebView の進捗を KVO する

WebView の読み込み進捗(estimatedProgress)を KVO する最小のコードは以下。これで正しく進捗を得ることを確認できた。

var observation: NSKeyValueObservation?

observation = webView.observe(\.estimatedProgress, options: .new){_, change in
      print("progress=\(String(describing: change.newValue))")
}

UI を実装する

WebView の進捗を得ることを確認できたので、それっぽい UI を実装してみる。要件は以下だ。

  • WKWebView 上部に UIProgressView で進捗を表示する
  • 進捗が 1.0 になったら(読み込みが完了したら)、プログレスの表示をフェードアウトして消したい(Chrome とか Safari のように)
  • プログレスの色をデフォルトの青から変えてみたい

コードは以下。

class ViewController: UIViewController, WKNavigationDelegate {
  
  @IBOutlet weak var webView: WKWebView!
  @IBOutlet weak var progressView: UIProgressView!
  
  private var observation: NSKeyValueObservation?
  private var colorCnt = 0
  private let colorArray: [UIColor] = [
    .blue,
    .green,
    .yellow,
    .red,
  ]
  
  override func viewDidLoad() {
    super.viewDidLoad()
    
    webView.load(URLRequest(url: URL(string: "https://www.kantei.go.jp/")!))
    
    progressView.progressTintColor = colorArray[colorCnt]
    colorCnt = colorCnt + 1
    
    observation = webView.observe(\.estimatedProgress, options: .new){_, change in
      print("progress=\(String(describing: change.newValue))")
      self.progressView.setProgress(Float(change.newValue!), animated: true)
      
      if change.newValue! >= 1.0 {
        UIView.animate(withDuration: 1.0,
                       delay: 0.0,
                       options: [.curveEaseIn],
                       animations: {
                        self.progressView.alpha = 0.0
                        
        }, completion: { (finished: Bool) in
          self.progressView.progressTintColor = self.colorArray[self.colorCnt]
          self.colorCnt = self.colorCnt + 1
          if self.colorCnt >= self.colorArray.count {
            self.colorCnt = 0
          }
          
          self.progressView.setProgress(0, animated: false)
        })
      }
      else {
        self.progressView.alpha = 1.0
      }
    }
    
  }
  
}

動作イメージ

上記のコードを実行するとこんな感じ。Good ですね。

完全なサンプルプロジェクト

ビルドできる完全なサンプルプロジェクトは GitHub にある