SwiftUI Как добавить индикатор активности в WKWebView? - PullRequest
2 голосов
/ 04 февраля 2020

Как добавить индикатор активности в WKWebView, который будет отображать индикатор во время загрузки веб-страницы и исчезать при загрузке?

Я просмотрел некоторые старые сообщения, но не смог понять, как сделать это в SwiftUI, см. ссылку на одно из старых решений ниже Как добавить индикатор активности в WKWebView (Swift 3)

Ответы [ 2 ]

0 голосов
/ 05 февраля 2020

Используя 3 шага, я делаю это в своем проекте.

Шаг 1: Создайте представление загрузки

import SwiftUI
import UIKit

struct ActivityIndicatorView: UIViewRepresentable {
    @Binding var isAnimating: Bool
    let style: UIActivityIndicatorView.Style

    func makeUIView(context: Context) -> UIActivityIndicatorView {
        return UIActivityIndicatorView(style: style)
    }

    func updateUIView(_ uiView: UIActivityIndicatorView, context: Context) {
        isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
    }
}

// Main View
struct LoadingView<Content>: View where Content: View {
    @Binding var isShowing: Bool
    let message: String
    var content: () -> Content

    var body: some View {
        GeometryReader { geometry in
            ZStack(alignment: .center) {
                self.content()
                    .disabled(self.isShowing)
                    .blur(radius: self.isShowing ? 3 : 0)

                VStack {
                    Text(self.message)
                        .bold()
                    ActivityIndicatorView(isAnimating: .constant(true), style: .large)
                }
                .frame(width: geometry.size.width / 2,
                       height: geometry.size.height / 5)
                .background(Color.secondary.colorInvert())
                .foregroundColor(Color.primary)
                .cornerRadius(20)
                .opacity(self.isShowing ? 1 : 0)
            }
        }
    }

}

// Mark: Testing

struct LoadingIndicator: View {
    var body: some View {
        LoadingView(isShowing: .constant(true), message: "Loading...") {
            NavigationView {
                List(["1", "2", "3", "4", "5"], id: \.self) { row in
                    Text(row)
                }.navigationBarTitle(Text("A List"), displayMode: .large)
            }
        }
    }
}

struct ActivityIndicatorView_Previews: PreviewProvider {
    static var previews: some View {
        LoadingIndicator()
    }
}

Шаг 2: Создайте WebView и WebViewModel

import SwiftUI
import WebKit

class WebViewModel: ObservableObject {
    @Published var isLoading: Bool = false
}

struct WebView: UIViewRepresentable {
    @ObservedObject var webViewModel: WebViewModel
    let urlString: String

    func makeUIView(context: Context) -> WKWebView {
        let wkWebView = WKWebView()
        if let url = URL(string: urlString) {
            let urlRequest = URLRequest(url: url)
            wkWebView.load(urlRequest)
        }
        return wkWebView
    }

    func updateUIView(_ wkWebView: WKWebView, context: Context) {
        // do nothing
    }

    class Coordinator: NSObject, WKNavigationDelegate {
        let webViewModel: WebViewModel

        init(_ webViewModel: WebViewModel) {
            self.webViewModel = webViewModel
        }

        func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
            webViewModel.isLoading = true
        }

        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            webViewModel.isLoading = false
        }
    }

    func makeCoordinator() -> WebView.Coordinator {
        Coordinator(webViewModel)
    }
}

struct WebView_Previews: PreviewProvider {
    static var previews: some View {
        WebView(webViewModel: WebViewModel(),
                urlString: "https://instagram.com/mahmudahsan/")
    }
}

Шаг 3: На главном экране используйте следующий код для отображения индикатора и веб-просмотра

ZStack {
        WebView(webViewModel: webViewModel, urlString: "http://ithinkdiff.net")
            .frame(height: 1000)

        if webViewModel.isLoading {
              LoadingView(isShowing: .constant(true), message: "Loading...") {
                            EmptyView()
              }
        }
}
0 голосов
/ 04 февраля 2020

Используйте UIViewRepresentable для создания UIActivityIndicatorView :

Вы можете контролировать анимацию индикатора активности, вызывая методы startAnimating() и stopAnimating(). Чтобы автоматически скрыть индикатор активности при остановке анимации, установите для свойства hidesWhenStopped значение true.

Вы можете установить цвет индикатора активности с помощью свойства color.

struct ActivityIndicatorView: UIViewRepresentable {
    @Binding var isAnimating: Bool
    let style: UIActivityIndicatorView.Style

    func makeUIView(context: UIViewRepresentableContext<ActivityIndicatorView>) -> UIActivityIndicatorView {
        return UIActivityIndicatorView(style: style)
    }

    func updateUIView(_ uiView: UIActivityIndicatorView, context: UIViewRepresentableContext<ActivityIndicatorView>) {
        isAnimating ? uiView.startAnimating() : uiView.stopAnimating()
    }
}

Создайте LoadingView, чтобы позволить вам обернуть вокруг ваших представлений:

Это позволяет вам стилизовать содержимое представлений действий.

struct LoadingView<Content>: View where Content: View {
    @Binding var isShowing: Bool
    var content: () -> Content

    var body: some View {
        GeometryReader { geometry in
            ZStack(alignment: .center) {
                self.content()
                    .disabled(self.isShowing)
                    .blur(radius: self.isShowing ? 3 : 0)

                VStack {
                    Text("Loading...")
                    ActivityIndicatorView(isAnimating: .constant(true), style: .large)
                }
                .frame(width: geometry.size.width / 2, height: geometry.size.height / 5)
                .background(Color.secondary.colorInvert())
                .foregroundColor(Color.red)
                .cornerRadius(20)
                .opacity(self.isShowing ? 1 : 0)

            }
        }
    }
}

Если вы хотите иметь возможность обновлять статус LoadingView(...), который вам потребуется для представления модели представления, которая наследует от ObservableObject:

На основе этого ответа: { ссылка }

class WebViewModel: ObservableObject {
    @Published var url: String
    @Published var isLoading: Bool = true

    init (url: String) {
        self.url = url
    }
}

struct WebView: UIViewRepresentable {
    @ObservedObject var viewModel: WebViewModel
    let webView = WKWebView()

    func makeCoordinator() -> Coordinator {
        Coordinator(self.viewModel)
    }

    class Coordinator: NSObject, WKNavigationDelegate {
        private var viewModel: WebViewModel

        init(_ viewModel: WebViewModel) {
            self.viewModel = viewModel
        }

        func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
            self.viewModel.isLoading = false
        }
    }

    func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<WebView>) { }

    func makeUIView(context: Context) -> UIView {
        self.webView.navigationDelegate = context.coordinator

        if let url = URL(string: self.viewModel.url) {
            self.webView.load(URLRequest(url: url))
        }

        return self.webView
    }
}

Затем, чтобы использовать его внутри своих представлений, вы должны сделать следующее:

struct ContentView: View {
    @ObservedObject var model = WebViewModel(url: "http://www.google.com")

    var body: some View {
        LoadingView(isShowing: self.$model.isLoading) {
            WebView(viewModel: self.model)
        }
    }
}
...