Search code examples
webviewswiftuixcode11uiactivityindicatorview

Load an Activity Indicator while website is loading in SwiftUI


I have a basic list that displays a webview. I want to add an activity indicator that shows while the webpage is loading. this is the code that I've created.

import SwiftUI
import WebKit

struct WebView: UIViewRepresentable {
    var url: String

    // makeUIView func
    func makeUIView(context: Context) -> WKWebView {

        guard let url = URL(string: self.url) else {
            return WKWebView()
        }

        let request = URLRequest(url: url)
        let wkWebView = WKWebView()
        wkWebView.load(request)
        return wkWebView
    }

    // updateUIView func
    func updateUIView(_ uiView: WKWebView, context: Context) {
    }

}


struct WebView_Preview: PreviewProvider {
    static var previews: some View {
        WebView(url: "https://www.google.com")
    }
}

Thank you!


Solution

  • Here's something that should do what you want. The WebView has a delegate in the Coordinator class. It changes a binding, to which the ContentView can react appropriately. Currently it's just a Text displaying the raw value of the state, but it can be replaced with an activity indicator of some sorts.

    struct ContentView: View {
        @State var urlString = ""
        @State var workState = WebView.WorkState.initial
    
        var body: some View {
            VStack(spacing: 20) {
                WebView(urlString: self.$urlString, workState: self.$workState)
    
                Button("Play") {
                    self.urlString = "https://www.example.com/"
                }
                Text("Current work = " + self.workState.rawValue)
            }
        }
    }
    
    struct WebView: UIViewRepresentable {
        enum WorkState: String {
            case initial
            case done
            case working
            case errorOccurred
        }
    
        @Binding var urlString: String
        @Binding var workState: WorkState
    
        func makeUIView(context: Context) -> WKWebView {
            let webView = WKWebView()
            webView.navigationDelegate = context.coordinator
            return webView
        }
    
        func updateUIView(_ uiView: WKWebView, context: Context) {
            switch self.workState {
            case .initial:
                if let url = URL(string: self.urlString) {
                    uiView.load(URLRequest(url: url))
                }
            default:
                break
            }
        }
    
        func makeCoordinator() -> Coordinator {
            Coordinator(self)
        }
    
        class Coordinator: NSObject, WKNavigationDelegate {
            var parent: WebView
    
            func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
              self.parent.workState = .working
            }
    
            func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
                self.parent.workState = .errorOccurred
            }
    
            func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
                self.parent.workState = .done
            }
    
            init(_ parent: WebView) {
                self.parent = parent
            }
        }
    }