【问题标题】:How to display estimatedProgress of WKWebView in a Linear style ProgressView in swiftUI?如何在swiftUI中以线性样式ProgressView显示WKWebView的估计进度?
【发布时间】:2021-12-10 05:27:11
【问题描述】:

我尝试在这件事上搜索数周,但找不到任何在 SwiftUI 中实现的帮助。大多数答案都是针对 UIKit 的。

struct ContentView: View {
    @State private var progress: Double = 0.0
    
    var body: some View {
        VStack {
            WebView()

            ProgressView(value: progress)
                .progressViewStyle(.linear)
        }
    }
}

struct WebView: UIViewRepresentable {
    func makeUIView(context: Context) -> WKWebView {
        let webView = WKWebView()
        webView.load(URLRequest(url: URL(string: "https://www.apple.com")!))
        return webView
    }
    
    func updateUIView(_ uiView: WKWebView, context: Context) {
        
    }
}

【问题讨论】:

    标签: swift swiftui wkwebview


    【解决方案1】:

    因为您使用UIViewRepresentable 来包装WKWebView,所以您需要使用UIKit 解决方案。

    我们可以使用 Key-Value-Observing (KVO) 跟踪 WKWebViewestimatedProgress。为此,我们需要向我们的WebView 添加一个协调器。

    仅仅添加协调器是不够的,我们需要一种方法将值从UIViewRepresentable 传递回View。我们可以使用@Binding 或闭包来做到这一点,但这会在视图更新时导致以下错误:

    警告:在视图更新期间修改状态,这将导致未定义的行为。

    “hacky”解决方案是使用 DispatchQueue.main.async 包装绑定的 progress 变量的设置,但这不是一个好的解决方案。

    解决此问题的更好方法是创建一个符合ObservableObject 的简单 ViewModel。

    这给出了以下代码:

    struct ContentView: View {
        @StateObject var viewModel = WebView.ProgressViewModel(progress: 0.0)
    
        var body: some View {
            VStack {
                WebView(url: URL(string: "https://www.apple.com")!, viewModel: viewModel)
    
                ProgressView(value: viewModel.progress)
                    .progressViewStyle(.linear)
            }
        }
    }
    

    ContentView 现在包含对 ProgressViewModel 的引用,它与 URL 一起传递给 WebViewProgressViewProgressViewModel 传递@Published 进度。

    struct WebView: UIViewRepresentable {
    
        let url: URL
        @ObservedObject var viewModel: ProgressViewModel
    
        private let webView = WKWebView()
    
        func makeUIView(context: Context) -> WKWebView {
            webView.load(URLRequest(url: url))
            return webView
        }
    
        func updateUIView(_ uiView: WKWebView, context: Context) {
    
        }
    }
    

    除了我们传递的是URLProgressViewModel 之外,这与您的UIViewRepresentable 没有太大区别。我们还从makeUIView 中提出了webView 的初始化,这样我们就可以在Coordinator 中访问它。

    extension WebView {
    
        func makeCoordinator() -> Coordinator {
            Coordinator(self, viewModel: viewModel)
        }
    
        class Coordinator: NSObject {
            private var parent: WebView
            private var viewModel: ProgressViewModel
            private var observer: NSKeyValueObservation?
    
            init(_ parent: WebView, viewModel: ProgressViewModel) {
                self.parent = parent
                self.viewModel = viewModel
                super.init()
    
                observer = self.parent.webView.observe(\.estimatedProgress) { [weak self] webView, _ in
                    guard let self = self else { return }
                    self.parent.viewModel.progress = webView.estimatedProgress
                }
            }
    
            deinit {
                observer = nil
            }
        }
    }
    

    WebView 上的此扩展创建了从 NSObject 继承的协调器。它包含对父级、WebView 和 viewModel 的引用。我们在初始化程序中设置了一个 KVO 观察者,观察 webView 上的 estimatedProgress,这允许我们更新 viewModel 上的进度值。

    extension WebView {
        class ProgressViewModel: ObservableObject {
            @Published var progress: Double = 0.0
    
            init (progress: Double) {
                self.progress = progress
            }
        }
    }
    

    最后,这是将UIViewRepresentableView 联系在一起的ProgressViewModel

    你可以看到它的视频here

    【讨论】:

    • 非常感谢您的回复。我唯一的问题是 [weak self] 和这行特定代码“guard let self = self else { return }”的目的。比如为什么协调器变成了可选的?期待您的回复。
    • 在闭包中使用 self 时,您应该弱化它,否则您将面临保留周期的风险。当您使用[weak self] 时,它使self 成为可选的,因此需要解开它。但在这种情况下,您可以忽略guard let self = self else { return } 并改用self?.parent.viewModel.progress = webView.estimatedProgress(注意self 后面的?)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-07-03
    • 2023-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-15
    相关资源
    最近更新 更多