Swift・iOS

Swiftを中心に学んだことを記録に残すブログです。技術に関係ない記事もたまに書いています。

【SwiftUI】WKWebView内のページの読み込み状況をUIProgressViewで表示する

 

はじめに

WKWebViewの読み込み状況をUIProgressViewで表示する実装について試したので、記事に残します。

  

サンプルイメージ

f:id:hfoasi8fje3:20210421220725g:plain

 

開発環境

 

本題

実装方針

WKWebViewのサブビューとしてUIProgressViewを追加し、UIViewRepresentableを使ってSwiftUIのプロジェクトでも使えるようにする。

 

実装

WebView.swift
import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    var webView = WKWebView()
    var progressView = UIProgressView()
    
    let urlString: String
    
    class Coordinator: NSObject {
        var parent: WebView
        
        init(_ parent: WebView) {
            self.parent = parent
        }
        
        // WebViewの読み込み状況を監視する
        func addProgressObserver() {
            parent.webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)
        }
        
        override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
            // progressViewのアニメーション処理
            if keyPath == "estimatedProgress" {
                parent.progressView.alpha = 1.0
                parent.progressView.setProgress(Float(parent.webView.estimatedProgress), animated: true)
                
                if parent.webView.estimatedProgress >= 1.0 {
                    UIView.animate(withDuration: 0.2, delay: 0.0, options: [.curveEaseOut], animations: { [weak self] in
                        self?.parent.progressView.alpha = 0.0
                    }, completion: { (finished: Bool) in
                        self.parent.progressView.setProgress(0.0, animated: false)
                    })
                }
            }
        }
    }
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIView(context: Context) -> WKWebView {
        webView.addSubview(progressView)
        
        // UIProgressViewのレイアウト設定
        progressView.translatesAutoresizingMaskIntoConstraints = false
        progressView.widthAnchor.constraint(equalTo: webView.widthAnchor, multiplier: 1.0).isActive = true
        progressView.topAnchor.constraint(equalTo: webView.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
        progressView.leadingAnchor.constraint(equalTo: webView.leadingAnchor, constant: 0).isActive = true
        
        return webView
    }
    
    func updateUIView(_ webView: WKWebView, context: Context) {
        context.coordinator.addProgressObserver()
        
        guard let url = URL(string: urlString) else {
            return
        }
        let request = URLRequest(url: url)
        webView.load(request)
    }
}

 

ContentView.swift(WebViewの使用例)
import SwiftUI

struct ContentView: View {
    var body: some View {
        WebView(urlString: "{表示したいURL}")
    }
}

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

 

おわりに

今回のサンプルではUIProgressViewのレイアウトに問題があったり、SwiftUIのProgressViewを活用できていないなど課題が残っているので記事にするか迷ったのですが、本記事のテーマに関する外部の記事が少ないこともあり、一旦動くサンプルを共有することにも意味はあるかと思い記事にしました。

 

※このブログのWebViewに関する記事は、以下のリンクでまとめて見ることができます。

WebView カテゴリーの記事一覧 - Swift・iOS

 

参考